From 3ef68d9840cbef258086515d2d3558220c4beab8 Mon Sep 17 00:00:00 2001 From: sentientwaffle Date: Wed, 15 Feb 2012 07:51:45 -0700 Subject: [PATCH] initial commit --- .gitignore | 2 + .npmignore | 0 LICENSE | 20 ++ README.md | 248 ++++++++++++++++ package.json | 31 ++ src/actor.coffee | 16 ++ src/blob.coffee | 17 ++ src/commit.coffee | 100 +++++++ src/diff.coffee | 61 ++++ src/git.coffee | 76 +++++ src/index.coffee | 20 ++ src/ref.coffee | 47 +++ src/repo.coffee | 224 +++++++++++++++ src/status.coffee | 48 ++++ src/submodule.coffee | 59 ++++ src/tag.coffee | 62 ++++ src/tree.coffee | 103 +++++++ test/actor.test.coffee | 36 +++ test/blob.test.coffee | 45 +++ test/commit.test.coffee | 35 +++ test/diff.test.coffee | 98 +++++++ test/fixtures.coffee | 12 + test/fixtures/branched/COMMIT_EDITMSG | 1 + test/fixtures/branched/HEAD | 1 + test/fixtures/branched/config | 5 + test/fixtures/branched/description | 1 + .../branched/hooks/applypatch-msg.sample | 15 + .../fixtures/branched/hooks/commit-msg.sample | 24 ++ .../branched/hooks/post-commit.sample | 8 + .../branched/hooks/post-receive.sample | 15 + .../branched/hooks/post-update.sample | 8 + .../branched/hooks/pre-applypatch.sample | 14 + .../fixtures/branched/hooks/pre-commit.sample | 46 +++ .../fixtures/branched/hooks/pre-rebase.sample | 169 +++++++++++ .../branched/hooks/prepare-commit-msg.sample | 36 +++ test/fixtures/branched/hooks/update.sample | 128 +++++++++ test/fixtures/branched/index | Bin 0 -> 184 bytes test/fixtures/branched/info/exclude | 6 + test/fixtures/branched/logs/HEAD | 7 + test/fixtures/branched/logs/refs/heads/master | 2 + .../branched/logs/refs/heads/something | 2 + .../17/2b99dd464f6c9c101a987991fcc63491f08985 | Bin 0 -> 85 bytes .../6f/1de2421ce0427d9243bcd4ba7e38734cf96fe4 | Bin 0 -> 19 bytes .../77/83d64e077617ab7b84fe9726f721e921b9623b | Bin 0 -> 151 bytes .../91/3318e66e9beed3e89e9c402c1d6585ef3f7e6f | 2 + .../9a/bc538b0dc8bf3974e04f3cb5d3f06240e8cef6 | Bin 0 -> 54 bytes .../b0/45ec4b14c60f03329654266478b6e8b92b33c5 | Bin 0 -> 121 bytes .../b6/1b4728ef3cb2b3de0ec0421a54d170b6b7de13 | Bin 0 -> 54 bytes .../bd/502d58e9f0d51bd6c880994f778d0c896e4135 | Bin 0 -> 51 bytes .../c0/efd1cc406c54014067a7da9ab15c6a9c1bc714 | Bin 0 -> 20 bytes .../e4/ff69dd8f19d770e9731b4bc424ccb695f0b5ad | Bin 0 -> 19 bytes test/fixtures/branched/refs/heads/master | 1 + test/fixtures/branched/refs/heads/something | 1 + test/fixtures/checkout/COMMIT_EDITMSG | 1 + test/fixtures/checkout/HEAD | 1 + test/fixtures/checkout/config | 5 + test/fixtures/checkout/description | 1 + .../checkout/hooks/applypatch-msg.sample | 15 + .../fixtures/checkout/hooks/commit-msg.sample | 24 ++ .../checkout/hooks/post-commit.sample | 8 + .../checkout/hooks/post-receive.sample | 15 + .../checkout/hooks/post-update.sample | 8 + .../checkout/hooks/pre-applypatch.sample | 14 + .../fixtures/checkout/hooks/pre-commit.sample | 46 +++ .../fixtures/checkout/hooks/pre-rebase.sample | 169 +++++++++++ .../checkout/hooks/prepare-commit-msg.sample | 36 +++ test/fixtures/checkout/hooks/update.sample | 128 +++++++++ test/fixtures/checkout/index | Bin 0 -> 104 bytes test/fixtures/checkout/info/exclude | 6 + test/fixtures/checkout/logs/HEAD | 100 +++++++ test/fixtures/checkout/logs/refs/heads/b2 | 1 + test/fixtures/checkout/logs/refs/heads/b3 | 1 + test/fixtures/checkout/logs/refs/heads/master | 1 + .../5d/0a23d3d71875e5abe637a1b7b3b9ea050f16aa | Bin 0 -> 125 bytes .../e5/fa16f39fe47d5562b74c4f5912d8ef07dd126a | Bin 0 -> 51 bytes .../e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 | Bin 0 -> 15 bytes test/fixtures/checkout/refs/heads/b2 | 1 + test/fixtures/checkout/refs/heads/b3 | 1 + test/fixtures/checkout/refs/heads/master | 1 + test/fixtures/remotes/HEAD | 1 + test/fixtures/remotes/config | 11 + test/fixtures/remotes/description | 1 + .../remotes/hooks/applypatch-msg.sample | 15 + test/fixtures/remotes/hooks/commit-msg.sample | 24 ++ .../fixtures/remotes/hooks/post-commit.sample | 8 + .../remotes/hooks/post-receive.sample | 15 + .../fixtures/remotes/hooks/post-update.sample | 8 + .../remotes/hooks/pre-applypatch.sample | 14 + test/fixtures/remotes/hooks/pre-commit.sample | 46 +++ test/fixtures/remotes/hooks/pre-rebase.sample | 169 +++++++++++ .../remotes/hooks/prepare-commit-msg.sample | 36 +++ test/fixtures/remotes/hooks/update.sample | 128 +++++++++ test/fixtures/remotes/index | Bin 0 -> 264 bytes test/fixtures/remotes/info/exclude | 6 + test/fixtures/remotes/logs/HEAD | 1 + test/fixtures/remotes/logs/refs/heads/master | 1 + ...24024466408b565d66ecd5902c84ec2362e513.idx | Bin 0 -> 1744 bytes ...4024466408b565d66ecd5902c84ec2362e513.pack | Bin 0 -> 76147 bytes test/fixtures/remotes/packed-refs | 2 + test/fixtures/remotes/refs/heads/master | 1 + .../fixtures/remotes/refs/remotes/origin/HEAD | 1 + test/fixtures/simple/HEAD | 1 + test/fixtures/simple/config | 5 + test/fixtures/simple/description | 1 + .../simple/hooks/applypatch-msg.sample | 15 + test/fixtures/simple/hooks/commit-msg.sample | 24 ++ test/fixtures/simple/hooks/post-commit.sample | 8 + .../fixtures/simple/hooks/post-receive.sample | 15 + test/fixtures/simple/hooks/post-update.sample | 8 + .../simple/hooks/pre-applypatch.sample | 14 + test/fixtures/simple/hooks/pre-commit.sample | 46 +++ test/fixtures/simple/hooks/pre-rebase.sample | 169 +++++++++++ .../simple/hooks/prepare-commit-msg.sample | 36 +++ test/fixtures/simple/hooks/update.sample | 128 +++++++++ test/fixtures/simple/index | Bin 0 -> 32 bytes test/fixtures/simple/info/exclude | 6 + test/fixtures/status/COMMIT_EDITMSG | 1 + test/fixtures/status/HEAD | 1 + test/fixtures/status/config | 5 + test/fixtures/status/description | 1 + .../status/hooks/applypatch-msg.sample | 15 + test/fixtures/status/hooks/commit-msg.sample | 24 ++ test/fixtures/status/hooks/post-commit.sample | 8 + .../fixtures/status/hooks/post-receive.sample | 15 + test/fixtures/status/hooks/post-update.sample | 8 + .../status/hooks/pre-applypatch.sample | 14 + test/fixtures/status/hooks/pre-commit.sample | 46 +++ test/fixtures/status/hooks/pre-rebase.sample | 169 +++++++++++ .../status/hooks/prepare-commit-msg.sample | 36 +++ test/fixtures/status/hooks/update.sample | 128 +++++++++ test/fixtures/status/index | Bin 0 -> 184 bytes test/fixtures/status/info/exclude | 6 + test/fixtures/status/logs/HEAD | 1 + test/fixtures/status/logs/refs/heads/master | 1 + .../10/c14fdc111b6776b1fc3740e8b16939233f7a84 | Bin 0 -> 126 bytes .../19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf | Bin 0 -> 19 bytes .../8b/aef1b4abc478178b004d62031cf7fe6db6f903 | Bin 0 -> 19 bytes .../ac/be86c7c89586e0912a0a851bacf309c595c308 | Bin 0 -> 20 bytes .../d8/b85abf216872cda37b67d8f0c2d1a6fa81a7c3 | Bin 0 -> 116 bytes .../e6/2415c2695333e16ce6a65eac0b0ade587fd391 | Bin 0 -> 19 bytes test/fixtures/status/refs/heads/master | 1 + test/fixtures/submodule/COMMIT_EDITMSG | 1 + test/fixtures/submodule/HEAD | 1 + test/fixtures/submodule/config | 5 + test/fixtures/submodule/description | 1 + .../submodule/hooks/applypatch-msg.sample | 15 + .../submodule/hooks/commit-msg.sample | 24 ++ .../submodule/hooks/post-commit.sample | 8 + .../submodule/hooks/post-receive.sample | 15 + .../submodule/hooks/post-update.sample | 8 + .../submodule/hooks/pre-applypatch.sample | 14 + .../submodule/hooks/pre-commit.sample | 46 +++ .../submodule/hooks/pre-rebase.sample | 169 +++++++++++ .../submodule/hooks/prepare-commit-msg.sample | 36 +++ test/fixtures/submodule/hooks/update.sample | 128 +++++++++ test/fixtures/submodule/index | Bin 0 -> 264 bytes test/fixtures/submodule/info/exclude | 6 + test/fixtures/submodule/logs/HEAD | 2 + .../fixtures/submodule/logs/refs/heads/master | 2 + .../3b/563fc33e799367e2d2824fd21e0219fc7591db | Bin 0 -> 92 bytes .../45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 | Bin 0 -> 18 bytes .../48/7a70ae6cae1041f9903701d0cd8c9f30dd88a9 | Bin 0 -> 125 bytes .../b0/e66a8a93b83161375f18dcdc9e9329af61e04f | Bin 0 -> 51 bytes .../ea/4ab982469a4a539b773a091670224343db1eee | 2 + .../fb/eb68ceaa10264b36573022d784137bab4fa273 | 2 + test/fixtures/submodule/refs/heads/master | 1 + test/fixtures/tagged/COMMIT_EDITMSG | 1 + test/fixtures/tagged/HEAD | 1 + test/fixtures/tagged/config | 5 + test/fixtures/tagged/description | 1 + .../tagged/hooks/applypatch-msg.sample | 15 + test/fixtures/tagged/hooks/commit-msg.sample | 24 ++ test/fixtures/tagged/hooks/post-commit.sample | 8 + .../fixtures/tagged/hooks/post-receive.sample | 15 + test/fixtures/tagged/hooks/post-update.sample | 8 + .../tagged/hooks/pre-applypatch.sample | 14 + test/fixtures/tagged/hooks/pre-commit.sample | 46 +++ test/fixtures/tagged/hooks/pre-rebase.sample | 169 +++++++++++ .../tagged/hooks/prepare-commit-msg.sample | 36 +++ test/fixtures/tagged/hooks/update.sample | 128 +++++++++ test/fixtures/tagged/index | Bin 0 -> 104 bytes test/fixtures/tagged/info/exclude | 6 + test/fixtures/tagged/logs/HEAD | 6 + test/fixtures/tagged/logs/refs/heads/master | 6 + .../12/d411f1066eccc420e1f0361ecf88f8e2b65f5b | Bin 0 -> 53 bytes .../19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf | Bin 0 -> 19 bytes .../2c/2ecd34cc1d33002db81285b50396ab597d10bd | Bin 0 -> 53 bytes .../32/bbb351de16c3e404b3b7c77601c3d124e1e1a1 | Bin 0 -> 153 bytes .../41/0a16650aa2a129ed639f66b7ce17bd76b90e65 | Bin 0 -> 53 bytes .../48/082f72f087ce7e6fa75b9c41d7387daecd447b | Bin 0 -> 18 bytes .../68/ba9551aa06079093a6b868b7fac98ed6634ec0 | Bin 0 -> 53 bytes .../78/cb34985ee281f9b8008b556ad5b4dd36473b10 | Bin 0 -> 154 bytes .../81/c545efebe5f57d4cab2ba9ec294c4b0cadf672 | Bin 0 -> 20 bytes .../91/d6d39cd8638f1067989e0ab9c9c00ca378c952 | Bin 0 -> 123 bytes .../9f/358a4addefcab294b83e4282bfef1f9625a249 | Bin 0 -> 22 bytes .../bb/fb0e8e077ab9b37941097883f4e917ecca3d33 | 3 + .../d0/0491fd7e5bb6fa28c517a0bb32b8b506539d4d | Bin 0 -> 17 bytes .../d6/68469ca17a197feaa175a83f2f3de2abb781ad | Bin 0 -> 53 bytes .../e5/6e15bb7ddb6bd0b6d924b18fcee53d8713d7ea | Bin 0 -> 21 bytes .../e6/380addaa18a80d1ce0ed1ebdf3417a654f6fde | 3 + .../ea/1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c | 2 + .../eb/cbdf47e0ad6503d09f59cd8eeb76d9435a0396 | Bin 0 -> 153 bytes .../f8/44a55bbc98af479703c35aeb2837cf0e4eff1a | Bin 0 -> 53 bytes test/fixtures/tagged/refs/heads/master | 1 + test/fixtures/tagged/refs/tags/tag-1 | 1 + test/index.test.coffee | 10 + test/ref.test.coffee | 64 +++++ test/repo.test.coffee | 271 ++++++++++++++++++ test/status.test.coffee | 53 ++++ test/tag.test.coffee | 44 +++ test/tree.test.coffee | 119 ++++++++ 211 files changed, 5425 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 package.json create mode 100644 src/actor.coffee create mode 100644 src/blob.coffee create mode 100644 src/commit.coffee create mode 100644 src/diff.coffee create mode 100644 src/git.coffee create mode 100644 src/index.coffee create mode 100644 src/ref.coffee create mode 100644 src/repo.coffee create mode 100644 src/status.coffee create mode 100644 src/submodule.coffee create mode 100644 src/tag.coffee create mode 100644 src/tree.coffee create mode 100644 test/actor.test.coffee create mode 100644 test/blob.test.coffee create mode 100644 test/commit.test.coffee create mode 100644 test/diff.test.coffee create mode 100644 test/fixtures.coffee create mode 100644 test/fixtures/branched/COMMIT_EDITMSG create mode 100644 test/fixtures/branched/HEAD create mode 100644 test/fixtures/branched/config create mode 100644 test/fixtures/branched/description create mode 100755 test/fixtures/branched/hooks/applypatch-msg.sample create mode 100755 test/fixtures/branched/hooks/commit-msg.sample create mode 100755 test/fixtures/branched/hooks/post-commit.sample create mode 100755 test/fixtures/branched/hooks/post-receive.sample create mode 100755 test/fixtures/branched/hooks/post-update.sample create mode 100755 test/fixtures/branched/hooks/pre-applypatch.sample create mode 100755 test/fixtures/branched/hooks/pre-commit.sample create mode 100755 test/fixtures/branched/hooks/pre-rebase.sample create mode 100755 test/fixtures/branched/hooks/prepare-commit-msg.sample create mode 100755 test/fixtures/branched/hooks/update.sample create mode 100644 test/fixtures/branched/index create mode 100644 test/fixtures/branched/info/exclude create mode 100644 test/fixtures/branched/logs/HEAD create mode 100644 test/fixtures/branched/logs/refs/heads/master create mode 100644 test/fixtures/branched/logs/refs/heads/something create mode 100644 test/fixtures/branched/objects/17/2b99dd464f6c9c101a987991fcc63491f08985 create mode 100644 test/fixtures/branched/objects/6f/1de2421ce0427d9243bcd4ba7e38734cf96fe4 create mode 100644 test/fixtures/branched/objects/77/83d64e077617ab7b84fe9726f721e921b9623b create mode 100644 test/fixtures/branched/objects/91/3318e66e9beed3e89e9c402c1d6585ef3f7e6f create mode 100644 test/fixtures/branched/objects/9a/bc538b0dc8bf3974e04f3cb5d3f06240e8cef6 create mode 100644 test/fixtures/branched/objects/b0/45ec4b14c60f03329654266478b6e8b92b33c5 create mode 100644 test/fixtures/branched/objects/b6/1b4728ef3cb2b3de0ec0421a54d170b6b7de13 create mode 100644 test/fixtures/branched/objects/bd/502d58e9f0d51bd6c880994f778d0c896e4135 create mode 100644 test/fixtures/branched/objects/c0/efd1cc406c54014067a7da9ab15c6a9c1bc714 create mode 100644 test/fixtures/branched/objects/e4/ff69dd8f19d770e9731b4bc424ccb695f0b5ad create mode 100644 test/fixtures/branched/refs/heads/master create mode 100644 test/fixtures/branched/refs/heads/something create mode 100644 test/fixtures/checkout/COMMIT_EDITMSG create mode 100644 test/fixtures/checkout/HEAD create mode 100644 test/fixtures/checkout/config create mode 100644 test/fixtures/checkout/description create mode 100755 test/fixtures/checkout/hooks/applypatch-msg.sample create mode 100755 test/fixtures/checkout/hooks/commit-msg.sample create mode 100755 test/fixtures/checkout/hooks/post-commit.sample create mode 100755 test/fixtures/checkout/hooks/post-receive.sample create mode 100755 test/fixtures/checkout/hooks/post-update.sample create mode 100755 test/fixtures/checkout/hooks/pre-applypatch.sample create mode 100755 test/fixtures/checkout/hooks/pre-commit.sample create mode 100755 test/fixtures/checkout/hooks/pre-rebase.sample create mode 100755 test/fixtures/checkout/hooks/prepare-commit-msg.sample create mode 100755 test/fixtures/checkout/hooks/update.sample create mode 100644 test/fixtures/checkout/index create mode 100644 test/fixtures/checkout/info/exclude create mode 100644 test/fixtures/checkout/logs/HEAD create mode 100644 test/fixtures/checkout/logs/refs/heads/b2 create mode 100644 test/fixtures/checkout/logs/refs/heads/b3 create mode 100644 test/fixtures/checkout/logs/refs/heads/master create mode 100644 test/fixtures/checkout/objects/5d/0a23d3d71875e5abe637a1b7b3b9ea050f16aa create mode 100644 test/fixtures/checkout/objects/e5/fa16f39fe47d5562b74c4f5912d8ef07dd126a create mode 100644 test/fixtures/checkout/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 create mode 100644 test/fixtures/checkout/refs/heads/b2 create mode 100644 test/fixtures/checkout/refs/heads/b3 create mode 100644 test/fixtures/checkout/refs/heads/master create mode 100644 test/fixtures/remotes/HEAD create mode 100644 test/fixtures/remotes/config create mode 100644 test/fixtures/remotes/description create mode 100755 test/fixtures/remotes/hooks/applypatch-msg.sample create mode 100755 test/fixtures/remotes/hooks/commit-msg.sample create mode 100755 test/fixtures/remotes/hooks/post-commit.sample create mode 100755 test/fixtures/remotes/hooks/post-receive.sample create mode 100755 test/fixtures/remotes/hooks/post-update.sample create mode 100755 test/fixtures/remotes/hooks/pre-applypatch.sample create mode 100755 test/fixtures/remotes/hooks/pre-commit.sample create mode 100755 test/fixtures/remotes/hooks/pre-rebase.sample create mode 100755 test/fixtures/remotes/hooks/prepare-commit-msg.sample create mode 100755 test/fixtures/remotes/hooks/update.sample create mode 100644 test/fixtures/remotes/index create mode 100644 test/fixtures/remotes/info/exclude create mode 100644 test/fixtures/remotes/logs/HEAD create mode 100644 test/fixtures/remotes/logs/refs/heads/master create mode 100644 test/fixtures/remotes/objects/pack/pack-3f24024466408b565d66ecd5902c84ec2362e513.idx create mode 100644 test/fixtures/remotes/objects/pack/pack-3f24024466408b565d66ecd5902c84ec2362e513.pack create mode 100644 test/fixtures/remotes/packed-refs create mode 100644 test/fixtures/remotes/refs/heads/master create mode 100644 test/fixtures/remotes/refs/remotes/origin/HEAD create mode 100644 test/fixtures/simple/HEAD create mode 100644 test/fixtures/simple/config create mode 100644 test/fixtures/simple/description create mode 100755 test/fixtures/simple/hooks/applypatch-msg.sample create mode 100755 test/fixtures/simple/hooks/commit-msg.sample create mode 100755 test/fixtures/simple/hooks/post-commit.sample create mode 100755 test/fixtures/simple/hooks/post-receive.sample create mode 100755 test/fixtures/simple/hooks/post-update.sample create mode 100755 test/fixtures/simple/hooks/pre-applypatch.sample create mode 100755 test/fixtures/simple/hooks/pre-commit.sample create mode 100755 test/fixtures/simple/hooks/pre-rebase.sample create mode 100755 test/fixtures/simple/hooks/prepare-commit-msg.sample create mode 100755 test/fixtures/simple/hooks/update.sample create mode 100644 test/fixtures/simple/index create mode 100644 test/fixtures/simple/info/exclude create mode 100644 test/fixtures/status/COMMIT_EDITMSG create mode 100644 test/fixtures/status/HEAD create mode 100644 test/fixtures/status/config create mode 100644 test/fixtures/status/description create mode 100755 test/fixtures/status/hooks/applypatch-msg.sample create mode 100755 test/fixtures/status/hooks/commit-msg.sample create mode 100755 test/fixtures/status/hooks/post-commit.sample create mode 100755 test/fixtures/status/hooks/post-receive.sample create mode 100755 test/fixtures/status/hooks/post-update.sample create mode 100755 test/fixtures/status/hooks/pre-applypatch.sample create mode 100755 test/fixtures/status/hooks/pre-commit.sample create mode 100755 test/fixtures/status/hooks/pre-rebase.sample create mode 100755 test/fixtures/status/hooks/prepare-commit-msg.sample create mode 100755 test/fixtures/status/hooks/update.sample create mode 100644 test/fixtures/status/index create mode 100644 test/fixtures/status/info/exclude create mode 100644 test/fixtures/status/logs/HEAD create mode 100644 test/fixtures/status/logs/refs/heads/master create mode 100644 test/fixtures/status/objects/10/c14fdc111b6776b1fc3740e8b16939233f7a84 create mode 100644 test/fixtures/status/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf create mode 100644 test/fixtures/status/objects/8b/aef1b4abc478178b004d62031cf7fe6db6f903 create mode 100644 test/fixtures/status/objects/ac/be86c7c89586e0912a0a851bacf309c595c308 create mode 100644 test/fixtures/status/objects/d8/b85abf216872cda37b67d8f0c2d1a6fa81a7c3 create mode 100644 test/fixtures/status/objects/e6/2415c2695333e16ce6a65eac0b0ade587fd391 create mode 100644 test/fixtures/status/refs/heads/master create mode 100644 test/fixtures/submodule/COMMIT_EDITMSG create mode 100644 test/fixtures/submodule/HEAD create mode 100644 test/fixtures/submodule/config create mode 100644 test/fixtures/submodule/description create mode 100755 test/fixtures/submodule/hooks/applypatch-msg.sample create mode 100755 test/fixtures/submodule/hooks/commit-msg.sample create mode 100755 test/fixtures/submodule/hooks/post-commit.sample create mode 100755 test/fixtures/submodule/hooks/post-receive.sample create mode 100755 test/fixtures/submodule/hooks/post-update.sample create mode 100755 test/fixtures/submodule/hooks/pre-applypatch.sample create mode 100755 test/fixtures/submodule/hooks/pre-commit.sample create mode 100755 test/fixtures/submodule/hooks/pre-rebase.sample create mode 100755 test/fixtures/submodule/hooks/prepare-commit-msg.sample create mode 100755 test/fixtures/submodule/hooks/update.sample create mode 100644 test/fixtures/submodule/index create mode 100644 test/fixtures/submodule/info/exclude create mode 100644 test/fixtures/submodule/logs/HEAD create mode 100644 test/fixtures/submodule/logs/refs/heads/master create mode 100644 test/fixtures/submodule/objects/3b/563fc33e799367e2d2824fd21e0219fc7591db create mode 100644 test/fixtures/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 create mode 100644 test/fixtures/submodule/objects/48/7a70ae6cae1041f9903701d0cd8c9f30dd88a9 create mode 100644 test/fixtures/submodule/objects/b0/e66a8a93b83161375f18dcdc9e9329af61e04f create mode 100644 test/fixtures/submodule/objects/ea/4ab982469a4a539b773a091670224343db1eee create mode 100644 test/fixtures/submodule/objects/fb/eb68ceaa10264b36573022d784137bab4fa273 create mode 100644 test/fixtures/submodule/refs/heads/master create mode 100644 test/fixtures/tagged/COMMIT_EDITMSG create mode 100644 test/fixtures/tagged/HEAD create mode 100644 test/fixtures/tagged/config create mode 100644 test/fixtures/tagged/description create mode 100755 test/fixtures/tagged/hooks/applypatch-msg.sample create mode 100755 test/fixtures/tagged/hooks/commit-msg.sample create mode 100755 test/fixtures/tagged/hooks/post-commit.sample create mode 100755 test/fixtures/tagged/hooks/post-receive.sample create mode 100755 test/fixtures/tagged/hooks/post-update.sample create mode 100755 test/fixtures/tagged/hooks/pre-applypatch.sample create mode 100755 test/fixtures/tagged/hooks/pre-commit.sample create mode 100755 test/fixtures/tagged/hooks/pre-rebase.sample create mode 100755 test/fixtures/tagged/hooks/prepare-commit-msg.sample create mode 100755 test/fixtures/tagged/hooks/update.sample create mode 100644 test/fixtures/tagged/index create mode 100644 test/fixtures/tagged/info/exclude create mode 100644 test/fixtures/tagged/logs/HEAD create mode 100644 test/fixtures/tagged/logs/refs/heads/master create mode 100644 test/fixtures/tagged/objects/12/d411f1066eccc420e1f0361ecf88f8e2b65f5b create mode 100644 test/fixtures/tagged/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf create mode 100644 test/fixtures/tagged/objects/2c/2ecd34cc1d33002db81285b50396ab597d10bd create mode 100644 test/fixtures/tagged/objects/32/bbb351de16c3e404b3b7c77601c3d124e1e1a1 create mode 100644 test/fixtures/tagged/objects/41/0a16650aa2a129ed639f66b7ce17bd76b90e65 create mode 100644 test/fixtures/tagged/objects/48/082f72f087ce7e6fa75b9c41d7387daecd447b create mode 100644 test/fixtures/tagged/objects/68/ba9551aa06079093a6b868b7fac98ed6634ec0 create mode 100644 test/fixtures/tagged/objects/78/cb34985ee281f9b8008b556ad5b4dd36473b10 create mode 100644 test/fixtures/tagged/objects/81/c545efebe5f57d4cab2ba9ec294c4b0cadf672 create mode 100644 test/fixtures/tagged/objects/91/d6d39cd8638f1067989e0ab9c9c00ca378c952 create mode 100644 test/fixtures/tagged/objects/9f/358a4addefcab294b83e4282bfef1f9625a249 create mode 100644 test/fixtures/tagged/objects/bb/fb0e8e077ab9b37941097883f4e917ecca3d33 create mode 100644 test/fixtures/tagged/objects/d0/0491fd7e5bb6fa28c517a0bb32b8b506539d4d create mode 100644 test/fixtures/tagged/objects/d6/68469ca17a197feaa175a83f2f3de2abb781ad create mode 100644 test/fixtures/tagged/objects/e5/6e15bb7ddb6bd0b6d924b18fcee53d8713d7ea create mode 100644 test/fixtures/tagged/objects/e6/380addaa18a80d1ce0ed1ebdf3417a654f6fde create mode 100644 test/fixtures/tagged/objects/ea/1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c create mode 100644 test/fixtures/tagged/objects/eb/cbdf47e0ad6503d09f59cd8eeb76d9435a0396 create mode 100644 test/fixtures/tagged/objects/f8/44a55bbc98af479703c35aeb2837cf0e4eff1a create mode 100644 test/fixtures/tagged/refs/heads/master create mode 100644 test/fixtures/tagged/refs/tags/tag-1 create mode 100644 test/index.test.coffee create mode 100644 test/ref.test.coffee create mode 100644 test/repo.test.coffee create mode 100644 test/status.test.coffee create mode 100644 test/tag.test.coffee create mode 100644 test/tree.test.coffee diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3063f07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +lib +node_modules diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d038e50 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012 [DJG](https://github.com/sentientwaffle) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1b50049 --- /dev/null +++ b/README.md @@ -0,0 +1,248 @@ +# Gift +A basic Node.js wrapper for the Git CLI. The API is based on +[Grit](https://github.com/mojombo/grit) + +# Installation + + $ npm install gift + +# API + + git = require 'gift' + + repo = git "path/to/repo" + # => # + +## Repo +### Repo#path +`String` - The path to the repository. + +### Repo#commits([treeish, [limit, [skip, ]]]callback) +Get a list of commits. + + * `treeish` - `String` (optional). + * `limit` - `Integer` (optional). + * `skip` - `Integer` (optional). + * `callback` - `Function` which receives `(err, commits)`, where `commits` is + an `Array` of `Commit`s. + +Get the 10 most recent commits to master. + + repo.commits (err, commits) -> + +Or to a different tag or branch. + + repo.commits "v0.0.3", (err, commits) -> + +Limit the maximum number of commits returned. + + repo.commits "master", 30, (err, commits) -> + +Skip some (for pagination): + + repo.commits "master", 30, 30, (err, commits) -> + +### Repo#tree([treeish]) => Tree +The `Tree` object for the treeish (which defaults to "master"). + + repo.tree().contents (err, children) -> + for child in children + console.log child.name + +### Repo#diff(commitA, commitB, [paths, ]callback) +Get the difference between the trees. + +The callback receives `(err, diffs)`. + +### Repo#remotes(callback) +Get the repository's remotes. + +Receives `(err, remotes)`, where each remote is a Ref. + +### Repo#remote_list(callback) +Get a list of the repository's remote names. + +Get the string names of each of the remotes. + +### Repo#remote_add(name, url, callback) +Equivalent to `git remote add `. + +### Repo#remote_fetch(name, callback) +`git fetch ` + + +### Repo#status(callback) +The callback receives `(err, status)`. + +### Repo#create_branch(name, callback) +Create a new branch with `name`, and call the callback when complete +with an error, if one occurred. + +### Repo#delete_branch(name, callback) +Delete the branch `name`, and call the callback with an error, if one occurred. + +### Repo#tags(callback) +Get a list of `Tag`s. + +### Repo#create_tag(name, callback) +Create a tab with the given name. + +### Repo#delete_tag(name, callback) +Delete the tag with the given name. + +### Repo#branches(callback) +`callback` receives `(err, heads)`. + +### Repo#create_branch(name, callback) +Create a branch with the given name. + +### Repo#delete_branch(delete, callback) +Delete the branch with the given name. + +### Repo#branch([branch, ]callback) +Get a branch. + + * `branch` - The name of the branch to get. Defaults to the + currently checked out branch. + * `callback` - Receives `(err, head)`. + + +### Repo#commit(message, [options, ]callback) +Commit some changes. + + * `message` - `String` + * `options` - + - `all` - `Boolean` + - `amend` - `Boolean` + * `callback` - Receives `(err)`. + +### Repo#add(files, callback) +`git add ` + +### Repo#remove(files, callback) +`git rm ` + +### Repo#checkout(treeish, callback) +`git checkout ` + +## Commit +### Commit#id +`String` - The commit's SHA. + +### Commit#parents +`Commit[]` + +### Commit#tree(callback) + + * `callback` - Receives `(err, tree)`. + +### Commit#author +`Actor` +### Commit#authored_date +`Date` +### Commit#committer +`Actor` +### Commit#committed_date +`Date` +### Commit#message +`String` + + +## Head +### Head#name +`String` + +### Head#commit +`Commit` + +## Tag +### Tag#name +`String` + +### Tag#commit +`Commit` + +### Tag#message(callback) +The callback receives `(err, message)` (`message` is a String). + +### Tag#tagger(callback) +The callback receives `(err, actor)`. + +### Tag#tag_date(callback) +The callback receives `(err, date)`. + +## Status +### Status#clean +`Boolean` + +### Status#files +`Object` - The keys are files, the values objects indicating whether or not +the file is staged, tracked, etc. + +Each file has the following properties: + + * `type` - "A" for added, "M" for modified, "D" for deleted. + * `staged` - `Boolean` + * `tracked` - `Boolean` + +## Actor +### Actor#name +`String` + +### Actor#email +`String` + + +## Tree +### Tree#id +`String` - SHA1 + +### Tree#contents(callback) + + * `callback` - Receives `(err, children)`. + * `children` - An array of `Blob`s, `Tree`s, and `Submodule`s. + +### Tree#blobs(callback) + + * `callback` - Receives `(err, child_blobs)`. + * `children` - `[Blob]` + +### Tree#trees(callback) + + * `callback` - Receives `(err, child_trees)`. + * `children` - `[Tree]` + +### Tree#find(directory, callback) + + * `directory` - `String` + * `callback` - Receives `(err, thing)`. + +## Blob +### Blob#id +`String` - SHA1 + +### Blob#mode +`String` + +### Blob#data(callback) + + * `callback` - `(err, data)` + +## Submodule +### Submodule#id +`String` + +### Submodule#name +`String` + +### Submodule#mode +`String` + +### Submodule#url(callback) +Get the url the submodule points to. + + +# License +See LICENSE. + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..2d5dad3 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ "name": "gift" +, "version": "0.0.1" +, "description": "a Git wrapper library" +, "keywords": ["git"] +, "homepage": "https://github.com/sentientwaffle/gift" +, "bugs": {"url": "https://github.com/sentientwaffle/gift/issues"} +, "author": + { "name": "sentientwaffle" + , "url": "http://sentientwaffle.github.com/" + } + +, "main": "./lib/index" + +, "scripts": + { "test": "mocha" + , "prepublish": "coffee -o lib -c src" + } + +, "repository": + { "type": "git" + , "url": "https://github.com/sentientwaffle/gift.git" + } + +, "dependencies": {"underscore": "1.x.x"} +, "devDependencies": + { "should": "0.4.x" + , "mocha": "0.5.x" + } + +, "engines": {"node": "> 0.4.1"} +} diff --git a/src/actor.coffee b/src/actor.coffee new file mode 100644 index 0000000..1d0f782 --- /dev/null +++ b/src/actor.coffee @@ -0,0 +1,16 @@ +module.exports = class Actor + constructor: (@name, @email) -> + + # Public: Get a string representation of the Actor. + toString: -> + "#{@name} <#{@email}>" + + # Public: Parse an Actor from a "bla " string. + # + # Returns Actor. + @from_string: (str) -> + if /<.+>/.test str + [m, name, email] = /(.*) <(.+?)>/.exec str + return new Actor(name, email) + else + return new Actor(str, null) diff --git a/src/blob.coffee b/src/blob.coffee new file mode 100644 index 0000000..2f1d634 --- /dev/null +++ b/src/blob.coffee @@ -0,0 +1,17 @@ +path = require 'path' + +module.exports = class Blob + constructor: (@repo, attrs) -> + {@id, @name, @mode} = attrs + + # Public: Get the blob contents. + # + # callback - Receives `(err, data)`. + # + data: (callback) -> + @repo.git "cat-file", {p: true}, @id + , (err, stdout, stderr) -> + return callback err, stdout + + toString: -> + "#" diff --git a/src/commit.coffee b/src/commit.coffee new file mode 100644 index 0000000..56e5973 --- /dev/null +++ b/src/commit.coffee @@ -0,0 +1,100 @@ +_ = require 'underscore' +Actor = require './actor' +Tree = require './tree' + +module.exports = class Commit + constructor: (@repo, @id, parents, tree, @author, @authored_date, @committer, @committed_date, @message) -> + # Public: Get the commit's Tree. + # + # Returns Tree. + @tree = _.memoize => (new Tree this, tree) + + # Public: Get the Commit's parent Commits. + # + # Returns an Array of Commits. + @parents = _.memoize => + _.map parents, (parent) => + new Commit @repo, parent + + + # Public: Find the matching commits. + # + # callback - Receives `(err, commits)` + # + @find_all: (repo, ref, options, callback) -> + options = _.extend {pretty: "raw"}, options + repo.git "rev-list", options, ref + , (err, stdout, stderr) => + return callback err if err + return callback null, @parse_commits(repo, stdout) + + + @find: (repo, id, callback) -> + options = {pretty: "raw", "max-count": 1} + repo.git "rev-list", options, id + , (err, stdout, stderr) => + return callback err if err + return callback null, @parse_commits(repo, stdout)[0] + + + @find_commits: (repo, ids, callback) -> + commits = [] + next = (i) -> + if id = ids[i] + Commit.find repo, id, (err, commit) -> + return callback err if err + commits.push commit + next i + 1 + # Done: all commits loaded. + else + callback null, commits + next 0 + + + # Internal: Parse the commits from `git rev-list` + # + # Return Commit[] + @parse_commits: (repo, text) -> + commits = [] + lines = text.split "\n" + while lines.length + id = _.last lines.shift().split(" ") + tree = _.last lines.shift().split(" ") + + parents = [] + while /^parent/.test lines[0] + parents.push _.last lines.shift().split(" ") + + author_line = lines.shift() + if !/^committer /.test(lines[0]) + author_line.push lines.shift() + [author, authored_date] = @actor author_line + + committer_line = lines.shift() + if lines[0] && !/^encoding/.test(lines[0]) + committer_line.push lines.shift() + [committer, committed_date] = @actor committer_line + + # not doing anything with this yet, but it's sometimes there + if /^encoding/.test lines.first + encoding = _.last lines.shift().split(" ") + + lines.shift() + + message_lines = [] + while /^ {4}/.test lines[0] + message_lines.push lines.shift()[4..-1] + + while lines[0]? && !lines[0].length + lines.shift() + + commits.push new Commit(repo, id, parents, tree, author, authored_date, committer, committed_date, message_lines.join("\n")) + return commits + + + # Internal: Parse the actor. + # + # Returns [String name and email, Date] + @actor: (line) -> + [m, actor, epoch] = /^.+? (.*) (\d+) .*$/.exec line + return [Actor.from_string(actor), new Date(+epoch)] diff --git a/src/diff.coffee b/src/diff.coffee new file mode 100644 index 0000000..0b02fe8 --- /dev/null +++ b/src/diff.coffee @@ -0,0 +1,61 @@ +Blob = require './blob' + +module.exports = class Diff + constructor: (@repo, @a_path, @b_path, a_blob, b_blob + , @a_mode, @b_mode, @new_file, @deleted_file, @diff + , @renamed_file=false, @similarity_index=0) -> + @a_blob = new Blob @repo, {id: a_blob} if a_blob + @b_blob = new Blob @repo, {id: b_blob} if b_blob + + # Public: Parse the Diffs from the command output. + # + # text - String stdout of a `git diff` command. + # + # Returns Array of Diff. + @parse: (repo, text) -> + lines = text.split "\n" + diffs = [] + + while lines.length && lines[0] + [m, a_path, b_path] = ///^diff\s--git\sa/(.+?)\sb/(.+)$///.exec lines.shift() + + if /^old mode/.test lines[0] + [m, a_mode] = /^old mode (\d+)/.exec lines.shift() + [m, b_mode] = /^new mode (\d+)/.exec lines.shift() + + if !lines.length || /^diff --git/.test(lines[0]) + diffs.push new Diff(repo, a_path, b_path, null, null, a_mode, b_mode, false, false, null) + continue + + sim_index = 0 + new_file = false + deleted_file = false + renamed_file = false + + if /^new file/.test lines[0] + [m, b_mode] = /^new file mode (.+)$/.exec lines.shift() + a_mode = null + new_file = true + else if /^deleted file/.test lines[0] + [m, a_mode] = /^deleted file mode (.+)$/.exec lines.shift() + b_mode = null + deleted_file = true + else if m = /^similarity index (\d+)\%/.exec(lines[0]) + sim_index = m[1].to_i + renamed_file = true + # shift away the 2 `rename from/to ...` lines + lines.shift() + lines.shift() + + [m, a_blob, b_blob, b_mode] = ///^index\s([0-9A-Fa-f]+)\.\.([0-9A-Fa-f]+)\s?(.+)?$///.exec lines.shift() + b_mode = b_mode.trim() if b_mode + + diff_lines = [] + while lines[0] && !/^diff/.test(lines[0]) + diff_lines.push lines.shift() + diff = diff_lines.join "\n" + + diffs.push new Diff(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff, renamed_file, sim_index) + + return diffs + diff --git a/src/git.coffee b/src/git.coffee new file mode 100644 index 0000000..e177c0f --- /dev/null +++ b/src/git.coffee @@ -0,0 +1,76 @@ +fs = require 'fs' +{exec} = require 'child_process' + +module.exports = Git = (git_dir, dot_git) -> + dot_git ||= "#{git_dir}/.git" + + git = (command, options, args, callback) -> + [callback, args] = [args, callback] if !callback + [callback, options] = [options, callback] if !callback + options ?= {} + options = options_to_argv options + options = options.join " " + args ?= [] + args = args.join " " if args instanceof Array + bash = "#{Git.bin} #{command} #{options} #{args}" + exec bash, {cwd: git_dir}, callback + return bash + + + # Public: Get a list of the remote names. + # + # callback - Receives `(err, names)`. + # + git.list_remotes = (callback) -> + fs.readdir "#{dot_git}/refs/remotes", (err, files) -> + callback err, (files || []) + + + # Public: Get the ref data string. + # + # type - Such as `remote` or `tag`. + # callback - Receives `(err, stdout)`. + # + git.refs = (type, options, callback) -> + [callback, options] = [options, callback] if !callback + prefix = "refs/#{type}s/" + + git "show-ref", (err, text) -> + matches = [] + for line in (text || "").split("\n") + continue if !line + [id, name] = line.split(' ') + if name.substr(0, prefix.length) == prefix + matches.push "#{name.substr(prefix.length)} #{id}" + return callback err, matches.join("\n") + + return git + + +# Public: The `git` command. +Git.bin = "git" + + + +# Internal: Transform an Object into command line options. +# +# Returns an Array of String option arguments. +Git.options_to_argv = options_to_argv = (options) -> + argv = [] + for key, val of options + if key.length == 1 + if val == true + argv.push "-#{key}" + else if val == false + # ignore + else + argv.push "-#{key}" + argv.push val + else + if val == true + argv.push "--#{key}" + else if val == false + # ignore + else + argv.push "--#{key}=#{val}" + return argv diff --git a/src/index.coffee b/src/index.coffee new file mode 100644 index 0000000..77d72c9 --- /dev/null +++ b/src/index.coffee @@ -0,0 +1,20 @@ +{exec} = require 'child_process' +Repo = require './repo' + +# Public: Create a Repo from the given path. +# +# Returns Repo. +module.exports = Git = (path, bare=false) -> + return new Repo path, bare + + +# Public: Initialize a git repository. +# +# path - The directory to run `git init .` in. +# callback - Receives `(err, repo)`. +# +Git.init = (path, callback) -> + exec "git init .", {cwd: git_dir} + , (err, stdout, stderr) -> + return callback err if err + return callback err, (new Repo path) diff --git a/src/ref.coffee b/src/ref.coffee new file mode 100644 index 0000000..0b66484 --- /dev/null +++ b/src/ref.coffee @@ -0,0 +1,47 @@ +fs = require 'fs' +Commit = require './commit' + +exports.Ref = class Ref + constructor: (@name, @commit) -> + {@repo} = @commit + + # Public: Get a String representation of the Ref. + toString: -> + "#" + + # Internal: Find all refs. + # + # options - (optional). + # + # Returns Array of Ref. + @find_all: (repo, type, RefClass, callback) -> + repo.git.refs type, {}, (err, text) -> + return callback err if err + names = [] + ids = [] + for ref in text.split("\n") + continue if !ref + [name, id] = ref.split(' ') + names.push name + ids.push id + + Commit.find_commits repo, ids, (err, commits) -> + return callback err if err + refs = [] + for name, i in names + refs.push new RefClass name, commits[i] + return callback null, refs + + +exports.Head = class Head extends Ref + @find_all: (repo, callback) -> + Ref.find_all repo, "head", Head, callback + + @current: (repo, callback) -> + fs.readFile "#{repo.dot_git}/HEAD", (err, data) -> + return callback err if err + [m, branch] = /ref: refs\/heads\/([^\s]+)/.exec data + fs.readFile "#{repo.dot_git}/refs/heads/#{branch}", (err, id) -> + Commit.find repo, id, (err, commit) -> + return callback err if err + return callback null, (new Head branch, commit) diff --git a/src/repo.coffee b/src/repo.coffee new file mode 100644 index 0000000..0efc4bd --- /dev/null +++ b/src/repo.coffee @@ -0,0 +1,224 @@ +_ = require 'underscore' +cmd = require './git' +Commit = require './commit' +Tree = require './tree' +Diff = require './diff' +Tag = require './tag' +Status = require './status' + +{Ref, Head} = require './ref' + +module.exports = class Repo + constructor: (@path, @bare) -> + if @bare + @dot_git = @path + else + @dot_git = "#{@path}/.git" + @git = cmd @path, @dot_git + + + # Public: Get a list of commits. + # + # treeish - String (optional). + # limit - Integer (optional). + # skip - Integer (optional). + # callback - Function which receives `(err, commits)`, where `commits` is + # an Array of Commits. + # + # Examples + # + # # Get the 10 most recent commits to master. + # repo.commits (err, commits) -> + # + # # Or to a different tag or branch. + # repo.commits "v0.0.3", (err, commits) -> + # + # # Limit the maximum number of commits returned. + # repo.commits "master", 30, (err, commits) -> + # + # # Skip some (for pagination): + # repo.commits "master", 30, 30, (err, commits) -> + # + commits: (start, limit, skip, callback) -> + [skip, callback] = [callback, skip] if !callback + [limit, callback] = [callback, limit] if !callback + [start, callback] = [callback, start] if !callback + throw new Error "a callback is required" if !callback + start ?= "master" + limit ?= 10 + skip ?= 0 + + Commit.find_all this, start, {"max-count": limit, skip}, callback + + + # Public: The tree object for the treeish or master. + # + # treeish - String treeish (such as a branch or tag) (optional). + # + # Returns Tree. + tree: (treeish="master") -> + return new Tree this, treeish + + + # Public: Get the difference between the trees. + # + # commitA - A Commit. + # commitB - A Commit. + # paths - A list of String paths to restrict the difference to (optional). + # callback - A Function which receives `(err, diffs)`. + # + diff: (commitA, commitB, paths, callback) -> + [callback, paths] = [paths, callback] if !callback + paths ?= [] + @git "diff", {}, _.flatten([commitA, commitB, "--", paths]) + , (err, stdout, stderr) => + return callback err if err + return callback err, Diff.parse(this, stdout) + + + # Public: Get the repository's remotes. + # + # callback - Receives `(err, remotes)`. + # + remotes: (callback) -> + Ref.find_all this, "remote", Ref, callback + + # Public: List the repository's remotes. + # + # callback - Receives `(err, names)`. + # + remote_list: (callback) -> + @git.list_remotes callback + + # Public: Add a remote. + # + # name - String name of the remote. + # url - String url of the remote. + # callback - Receives `(err)` + # + remote_add: (name, url, callback) -> + @git "remote", {}, ["add", name, url] + , (err, stdout, stderr) -> + callback err + + # Public: `git fetch `. + # + # name - String name of the remote + # callback - Receives `(err)`. + # + remote_fetch: (name, callback) -> + @git "fetch", {}, name + , (err, stdout, stderr) -> + callback err + + + # Public: Get the repository's status (`git status`). + # + # callback - Receives `(err, callback)` + # + status: (callback) -> + return new Status(this, callback) + + + # Public: Get the repository's tags. + # + # callback - Receives `(err, tags)`. + # + tags: (callback) -> + Tag.find_all this, callback + + # Public: Create a tag. + # + # name - String + # callback - Receives `(err)`. + # + create_tag: (name, callback) -> + @git "tag", {a: name}, callback + + # Public: Delete the tag. + # + # name - String + # callback - Receives `(err)`. + # + delete_tag: (name, callback) -> + @git "tag", {d: name}, callback + + + # Public: Get a list of branches. + # + # callback - Receives `(err, heads)`. + # + branches: (callback) -> + Head.find_all this, callback + + # Public: Create a branch with the given name. + # + # name - String name of the new branch. + # callback - Receives `(err)`. + # + create_branch: (name, callback) -> + @git "branch", {}, name, (err, stdout, stderr) -> + return callback err + + # Public: Delete the branch with the given name. + # + # name - String name of the branch to delete. + # callback - Receives `(err)`. + # + delete_branch: (name, callback) -> + @git "branch", {d: true}, name, (err, stdout, stderr) -> + return callback err + + # Public: Get the Branch with the given name. + # + # name - String (optional). By default, get the current branch. + # callback - Receives `(err, head)` + # + branch: (name, callback) -> + [name, callback] = [callback, name] if !callback + if !name + Head.current this, callback + else + @branches (err, heads) -> + return callback err if err + for head in heads + return callback null, head if head.name == name + return callback new Error "No branch named '#{name}' found" + + + # Public: Checkout the treeish. + checkout: (treeish, callback) -> + @git "checkout", {}, treeish, callback + + + # Public: Commit some code. + # + # message - String + # options - Object (optional). + # "amend" - Boolean + # "all" - Boolean + # callback - Receives `(err)`. + # + commit: (message, options, callback) -> + [options, callback] = [callback, options] if !callback + options = _.extend options, {m: message} + @git "commit", options, (err, stdout, stderr) -> + callback err + + # Public: Add files to the index. + # + # files - Array of String paths; or a String path. + # callback - Receives `(err)`. + # + add: (files, callback) -> + files = [files] if _.isString files + @git "add", {}, files, callback + + # Public: Remove files from the index. + # + # files - Array of String paths; or a String path. + # callback - Receives `(err)`. + # + remove: (files, callback) -> + files = [files] if _.isString files + @git "rm", {}, files, callback diff --git a/src/status.coffee b/src/status.coffee new file mode 100644 index 0000000..169a816 --- /dev/null +++ b/src/status.coffee @@ -0,0 +1,48 @@ +# Public: Create a Status. +# +# repo - A Repo. +# callback - Receives `(err, status)` +# +module.exports = S = (repo, callback) -> + repo.git "status", (err, stdout, stderr) -> + status = new Status repo + status.parse stdout + return callback err, status + + +BEGIN_STAGED = "# Changes to be committed:" +BEGIN_UNSTAGED = "# Changed but not updated:" +BEGIN_UNTRACKED = "# Untracked files:" +FILE = /^#\s+([^\s]+)[:]\s+(.+)$/ +TYPES = + added: "A" + modified: "M" + deleted: "D" + +S.Status = class Status + constructor: (@repo) -> + + # Internal: Parse the status from stdout of a `git status` command. + parse: (text) -> + @files = {} + @clean = true + state = null + for line in text.split("\n") + @clean = false + if line == BEGIN_STAGED + state = "staged" + else if line == BEGIN_UNSTAGED + state = "unstaged" + else if line == BEGIN_UNTRACKED + state = "untracked" + else if state && match = FILE.exec(line) + file = match[2] + data = switch state + when "staged" then {staged: true, tracked: true} + when "unstaged" then {staged: false, tracked: true} + data.type = TYPES[match[1]] + @files[file] = data + else if state == "untracked" && (match = /^#\s+([^\s]+)$/.exec(line)) + file = match[1] + @files[file] = {tracked: false} + return diff --git a/src/submodule.coffee b/src/submodule.coffee new file mode 100644 index 0000000..bc4cef7 --- /dev/null +++ b/src/submodule.coffee @@ -0,0 +1,59 @@ +module.exports = class Submodule + constructor: (@repo, options) -> + {@id, @name, @mode} = options + + # Public: Get the URL of the submodule. + # + # treeish - String treeish to look up the url within. + # callback - Receives `(err, url)`. + # + url: (treeish, callback) -> + [treeish, callback] = [callback, treeish] if !callback + treeish ?= "master" + + Submodule.config @repo, treeish, (err, config) => + return callback err, config?[@name].url + + + # Internal: Parse the `.gitmodules` file. + # + # repo - A Repo. + # treeish - String + # callback - Receives `(err, config)`, where the config object has + # the submodule names as its keys. + # + # Examples + # + # The following `.gitmodules` file: + # + # [submodule "spoon-knife"] + # path = spoon-knife + # url = git://github.com/octocat/Spoon-Knife.git + # + # would parse to: + # + # { "spoon-knife": + # { "path": "spoon-knife" + # , "url": "git://github.com/octocat/Spoon-Knife.git" + # } + # } + # + @config: (repo, treeish, callback) -> + repo.tree(treeish).find ".gitmodules", (err, blob) -> + return callback err if err + blob.data (err, data) -> + return callback err if err + + conf = {} + lines = data.split "\n" + current = null + while lines.length + line = lines.shift() + + if match = /^\[submodule "(.+)"\]$/.exec line + current = match[1] + conf[current] = {} + else if match = /^\s+([^\s]+)\s+[=]\s+(.+)$/.exec line + conf[current][match[1]] = match[2] + + return callback null, conf diff --git a/src/tag.coffee b/src/tag.coffee new file mode 100644 index 0000000..937259a --- /dev/null +++ b/src/tag.coffee @@ -0,0 +1,62 @@ +_ = require 'underscore' +Commit = require './commit' +Actor = require './actor' +{Ref} = require './ref' + +module.exports = class Tag extends Ref + @find_all: (repo, callback) -> + Ref.find_all repo, "tag", Tag, callback + + + # Public: Get the tag message. + # + # Returns String. + message: (callback) -> + @lazy (err, data) -> + return callback err if err + return callback null, data.message + + # Public: Get the tag author. + # + # Returns Actor. + tagger: (callback) -> + @lazy (err, data) -> + return callback err if err + return callback null, data.tagger + + # Public: Get the date that the tag was created. + # + # Returns Date. + tag_date: (callback) -> + @lazy (err, data) -> + return callback err if err + return callback null, data.tag_date + + # Internal: Load the tag data. + lazy: (callback) -> + return callback null, @_lazy_data if @_lazy_data + @repo.git "cat-file", {}, ["tag", @name] + , (err, stdout, stderr) => + return callback err if err + lines = stdout.split "\n" + data = {} + + lines.shift() # object 4ae1cc5e6c7bb85b14ecdf221030c71d0654a42e + lines.shift() # type commit + lines.shift() # tag v0.0.2 + + # bob + author_line = lines.shift() + [m, author, epoch] = /^.+? (.*) (\d+) .*$/.exec author_line + + data.tagger = Actor.from_string author + data.tag_date = new Date epoch + + lines.shift() + message = [] + while line = lines.shift() + message.push line + data.message = message.join("\n") + + return callback null, (@_lazy_data = data) + diff --git a/src/tree.coffee b/src/tree.coffee new file mode 100644 index 0000000..3b4ccb5 --- /dev/null +++ b/src/tree.coffee @@ -0,0 +1,103 @@ +_ = require 'underscore' +Blob = require './blob' +Submodule = require './submodule' + +module.exports = class Tree + # repo - A Repo. + # options - An Object with properties "id", "name", and "mode"; + # or just a String id. + constructor: (@repo, options) -> + if _.isString options + @id = options + else + {@id, @name, @mode} = options + + + # Public: Get the children of the tree. + # + # callback - Receives `(err, children)`, where children is a list + # of Trees, Blobs, and Submodules. + # + contents: (callback) -> + return callback null, @_contents if @_contents + @repo.git "ls-tree", {}, @id + , (err, stdout, stderr) => + return callback err if err + @_contents = [] + for line in stdout.split("\n") + @_contents.push @content_from_string(line) if line + return callback null, @_contents + + + # Public: Get the child blobs. + # + # callback - Receives `(err, blobs)`. + # + blobs: (callback) -> + @contents (err, children) -> + return callback err if err + return callback null, _.filter children, (child) -> + child instanceof Blob + + + # Public: Get the child blobs. + # + # callback - Receives `(err, trees)`. + # + trees: (callback) -> + @contents (err, children) -> + return callback err if err + return callback null, _.filter children, (child) -> + child instanceof Tree + + + # Public: Find the named object in this tree's contents. + # + # callback - Receives `(err, obj)` where obj is Tree, Blob, or null + # if not found. + # + find: (file, callback) -> + if /\//.test file + [dir, rest] = file.split "/", 2 + @trees (err, _trees) => + for tree in _trees + return callback rest, callback if tree.name == dir + return callback null, null + else + @contents (err, children) -> + return callback err if err + for child in children + if child.name == file + return callback null, child + return callback null, null + + + # Internal: Parse a Blob or Tree from the line. + # + # line - String + # + # Examples + # + # tree.content_from_string "100644 blob e4ff69dd8f19d770e9731b4bc424ccb695f0b5ad README.md" + # # => # + # + # Returns Blob, Tree or Submodule. + content_from_string: (line) -> + [mode, type, id, name] = line.split /[\t ]+/, 4 + switch type + when "tree" + new Tree @repo, {id, name, mode} + when "blob" + new Blob @repo, {id, name, mode} + when "link" + new Blob @repo, {id, name, mode} + when "commit" + new Submodule @repo, {id, name, mode} + else + throw new Error "Invalid object type: '#{type}'" + + # Public: Get a String representation of the Tree. + # + # Returns String. + toString: -> + "#" diff --git a/test/actor.test.coffee b/test/actor.test.coffee new file mode 100644 index 0000000..26a6a91 --- /dev/null +++ b/test/actor.test.coffee @@ -0,0 +1,36 @@ +should = require 'should' +Actor = require '../src/actor' + +describe "Actor", -> + describe ".constructor", -> + actor = new Actor "bob", "bob@example.com" + it "assigns @name", -> + actor.name.should.eql "bob" + + it "assigns @email", -> + actor.email.should.eql "bob@example.com" + + + describe "#toString", -> + actor = new Actor "bob", "bob@example.com" + + it "is a string representation of the actor", -> + actor.toString().should.eql "bob " + + + describe ".from_string", -> + describe "with a name and email", -> + actor = Actor.from_string "bob " + it "parses the name", -> + actor.name.should.eql "bob" + + it "parses the email", -> + actor.email.should.eql "bob@example.com" + + describe "with only a name", -> + actor = Actor.from_string "bob" + it "parses the name", -> + actor.name.should.eql "bob" + + it "does not parse the email", -> + should.not.exist actor.email diff --git a/test/blob.test.coffee b/test/blob.test.coffee new file mode 100644 index 0000000..0bbdd43 --- /dev/null +++ b/test/blob.test.coffee @@ -0,0 +1,45 @@ +should = require 'should' +fixtures = require './fixtures' +git = require '../src' +Blob = require '../src/blob' + +describe "Blob", -> + describe "constructor", -> + repo = fixtures.branched + blob = new Blob repo, {name: "X", mode: "Y", id: "abc"} + it "assigns @name", -> + blob.name.should.eql "X" + it "assigns @mode", -> + blob.mode.should.eql "Y" + it "assigns @id", -> + blob.id.should.eql "abc" + + + describe "#data", -> + describe "of a file off the root", -> + repo = git "#{__dirname}/fixtures/branched" + data = null + before (done) -> + repo.tree().blobs (err, blobs) -> + blobs[0].data (err, _data) -> + data = _data + done err + + it "is a string", -> + data.should.be.a "string" + data.should.include "Bla" + + describe "of a file in a subdir", -> + repo = git "#{__dirname}/fixtures/branched" + data = null + before (done) -> + repo.tree().trees (err, trees) -> + trees[0].blobs (err, blobs) -> + blobs[0].data (err, _data) -> + data = _data + done err + + it "is a string", -> + data.should.be.a "string" + data.should.include "!!!" + diff --git a/test/commit.test.coffee b/test/commit.test.coffee new file mode 100644 index 0000000..81e7afb --- /dev/null +++ b/test/commit.test.coffee @@ -0,0 +1,35 @@ +should = require 'should' +fixtures = require './fixtures' +git = require '../src' +Commit = require '../src/commit' +Tree = require '../src/tree' + +describe "Commit", -> + describe "#tree", -> + repo = git "#{__dirname}/fixtures/branched" + tree = null + before (done) -> + repo.commits "master", (err, commits) -> + tree = commits[0].tree() + done err + + it "passes a tree", -> + tree.should.be.an.instanceof Tree + + + describe "#parents", -> + repo = fixtures.branched + parents = null + parent = null + before (done) -> + repo.commits "something", (err, commits) -> + parents = commits[0].parents() + parent = commits[1] + done err + + it "is an Array of Commits", -> + parents.should.be.an.instanceof Array + parents[0].should.be.an.instanceof Commit + + it "has the parent commit", -> + parents[0].id.should.eql parent.id diff --git a/test/diff.test.coffee b/test/diff.test.coffee new file mode 100644 index 0000000..465e676 --- /dev/null +++ b/test/diff.test.coffee @@ -0,0 +1,98 @@ +should = require 'should' +fixtures = require './fixtures' +git = require '../src' +Diff = require '../src/diff' +Blob = require '../src/blob' + + +describe "Diff", -> + describe ".parse", -> + describe "simple editing", -> + repo = fixtures.tagged + stdout = """ + diff --git a/file.txt b/file.txt + index d00491f..48082f7 100644 + --- a/file.txt + +++ b/file.txt + @@ -1 +1 @@ + -1 + +12 + """ + diffs = Diff.parse repo, stdout + + it "is an Array of Diffs", -> + diffs.should.be.an.instanceof Array + diffs[0].should.be.an.instanceof Diff + + it "has one diff", -> + diffs.should.have.lengthOf 1 + + describe "the first diff", -> + diff = diffs[0] + + it "has the repo", -> + diff.repo.should.eql repo + + for blob in ["a_blob", "b_blob"] + it "has a #{blob}", -> + diff[blob].should.be.an.instanceof Blob + + for path in ["a_path", "b_path"] + it "has a #{path}", -> + diff[path].should.eql "file.txt" + + it "has a b_mode", -> + diff.b_mode.should.eql "100644" + + for change in ["new_file", "renamed_file", "deleted_file"] + it "#{change} is false", -> + diff[change].should.be.false + + it "has a similarity_index of 0", -> + diff.similarity_index.should.eql 0 + + describe "delete a file", -> + repo = fixtures.branched + stdout = """ + diff --git a/README.md b/README.md + index e4ff69d..c0efd1c 100644 + --- a/README.md + +++ b/README.md + @@ -1 +1 @@ + -Bla + +Bla2 + diff --git a/some/hi.txt b/some/hi.txt + deleted file mode 100644 + index 6f1de24..0000000 + --- a/some/hi.txt + +++ /dev/null + @@ -1 +0,0 @@ + -!!! + """ + diffs = Diff.parse repo, stdout + + it "has 2 diffs", -> + diffs.should.have.lengthOf 2 + + describe "the second diff", -> + diff = diffs[1] + it "deletes a file", -> + diff.deleted_file.should.be.true + + describe "create a file", -> + repo = fixtures.branched + stdout = """ + diff --git a/some/hi.txt b/some/hi.txt + new file mode 100644 + index 0000000..6f1de24 + --- /dev/null + +++ b/some/hi.txt + @@ -0,0 +1 @@ + +!!! + """ + diffs = Diff.parse repo, stdout + + it "creates a file", -> + diffs[0].new_file.should.be.true + + diff --git a/test/fixtures.coffee b/test/fixtures.coffee new file mode 100644 index 0000000..e7f5067 --- /dev/null +++ b/test/fixtures.coffee @@ -0,0 +1,12 @@ +git = require '../src' + +dir = "#{__dirname}/fixtures" + +module.exports = + branched: git("#{dir}/branched", true) + checkout: git("#{dir}/checkout", true) + remotes: git("#{dir}/remotes", true) + simple: git("#{dir}/simple", true) + status: git("#{dir}/status", true) + submodule: git("#{dir}/submodule", true) + tagged: git("#{dir}/tagged", true) diff --git a/test/fixtures/branched/COMMIT_EDITMSG b/test/fixtures/branched/COMMIT_EDITMSG new file mode 100644 index 0000000..3bda497 --- /dev/null +++ b/test/fixtures/branched/COMMIT_EDITMSG @@ -0,0 +1 @@ +add a sub dir diff --git a/test/fixtures/branched/HEAD b/test/fixtures/branched/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/test/fixtures/branched/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/test/fixtures/branched/config b/test/fixtures/branched/config new file mode 100644 index 0000000..515f483 --- /dev/null +++ b/test/fixtures/branched/config @@ -0,0 +1,5 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true diff --git a/test/fixtures/branched/description b/test/fixtures/branched/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/test/fixtures/branched/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/test/fixtures/branched/hooks/applypatch-msg.sample b/test/fixtures/branched/hooks/applypatch-msg.sample new file mode 100755 index 0000000..8b2a2fe --- /dev/null +++ b/test/fixtures/branched/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +test -x "$GIT_DIR/hooks/commit-msg" && + exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +: diff --git a/test/fixtures/branched/hooks/commit-msg.sample b/test/fixtures/branched/hooks/commit-msg.sample new file mode 100755 index 0000000..6ef1d29 --- /dev/null +++ b/test/fixtures/branched/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by git-commit with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/test/fixtures/branched/hooks/post-commit.sample b/test/fixtures/branched/hooks/post-commit.sample new file mode 100755 index 0000000..2266821 --- /dev/null +++ b/test/fixtures/branched/hooks/post-commit.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script that is called after a successful +# commit is made. +# +# To enable this hook, rename this file to "post-commit". + +: Nothing diff --git a/test/fixtures/branched/hooks/post-receive.sample b/test/fixtures/branched/hooks/post-receive.sample new file mode 100755 index 0000000..7a83e17 --- /dev/null +++ b/test/fixtures/branched/hooks/post-receive.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script for the "post-receive" event. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for a sample, or uncomment the next line and +# rename the file to "post-receive". + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/test/fixtures/branched/hooks/post-update.sample b/test/fixtures/branched/hooks/post-update.sample new file mode 100755 index 0000000..5323b56 --- /dev/null +++ b/test/fixtures/branched/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git-update-server-info diff --git a/test/fixtures/branched/hooks/pre-applypatch.sample b/test/fixtures/branched/hooks/pre-applypatch.sample new file mode 100755 index 0000000..b1f187c --- /dev/null +++ b/test/fixtures/branched/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: diff --git a/test/fixtures/branched/hooks/pre-commit.sample b/test/fixtures/branched/hooks/pre-commit.sample new file mode 100755 index 0000000..439eefd --- /dev/null +++ b/test/fixtures/branched/hooks/pre-commit.sample @@ -0,0 +1,46 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by git-commit with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git-rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test "$(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0')" +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +exec git diff-index --check --cached $against -- diff --git a/test/fixtures/branched/hooks/pre-rebase.sample b/test/fixtures/branched/hooks/pre-rebase.sample new file mode 100755 index 0000000..be1b06e --- /dev/null +++ b/test/fixtures/branched/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git-rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/test/fixtures/branched/hooks/prepare-commit-msg.sample b/test/fixtures/branched/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..3652424 --- /dev/null +++ b/test/fixtures/branched/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/test/fixtures/branched/hooks/update.sample b/test/fixtures/branched/hooks/update.sample new file mode 100755 index 0000000..fd63b2d --- /dev/null +++ b/test/fixtures/branched/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git-cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/test/fixtures/branched/index b/test/fixtures/branched/index new file mode 100644 index 0000000000000000000000000000000000000000..ffbc9328701de6e504bcde16109bf8855c33b0a4 GIT binary patch literal 184 zcmZ?q402{*U|<4ae{|bQB0^Db4@9-oe3Mk1dVA^2Yjusdvo20RZV;J|X}B literal 0 HcmV?d00001 diff --git a/test/fixtures/branched/info/exclude b/test/fixtures/branched/info/exclude new file mode 100644 index 0000000..2c87b72 --- /dev/null +++ b/test/fixtures/branched/info/exclude @@ -0,0 +1,6 @@ +# git-ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/test/fixtures/branched/logs/HEAD b/test/fixtures/branched/logs/HEAD new file mode 100644 index 0000000..e952554 --- /dev/null +++ b/test/fixtures/branched/logs/HEAD @@ -0,0 +1,7 @@ +0000000000000000000000000000000000000000 b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999490 -0700 commit (initial): 1 +b045ec4b14c60f03329654266478b6e8b92b33c5 b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999503 -0700 checkout: moving from master to something +b045ec4b14c60f03329654266478b6e8b92b33c5 7783d64e077617ab7b84fe9726f721e921b9623b sentientwaffle 1328999518 -0700 commit: 2 +7783d64e077617ab7b84fe9726f721e921b9623b b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999521 -0700 checkout: moving from something to master +b045ec4b14c60f03329654266478b6e8b92b33c5 7783d64e077617ab7b84fe9726f721e921b9623b sentientwaffle 1329001555 -0700 checkout: moving from master to something +7783d64e077617ab7b84fe9726f721e921b9623b b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1329013635 -0700 checkout: moving from something to master +b045ec4b14c60f03329654266478b6e8b92b33c5 913318e66e9beed3e89e9c402c1d6585ef3f7e6f sentientwaffle 1329013671 -0700 commit: add a sub dir diff --git a/test/fixtures/branched/logs/refs/heads/master b/test/fixtures/branched/logs/refs/heads/master new file mode 100644 index 0000000..465dd57 --- /dev/null +++ b/test/fixtures/branched/logs/refs/heads/master @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999490 -0700 commit (initial): 1 +b045ec4b14c60f03329654266478b6e8b92b33c5 913318e66e9beed3e89e9c402c1d6585ef3f7e6f sentientwaffle 1329013671 -0700 commit: add a sub dir diff --git a/test/fixtures/branched/logs/refs/heads/something b/test/fixtures/branched/logs/refs/heads/something new file mode 100644 index 0000000..e7fada5 --- /dev/null +++ b/test/fixtures/branched/logs/refs/heads/something @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999503 -0700 branch: Created from HEAD +b045ec4b14c60f03329654266478b6e8b92b33c5 7783d64e077617ab7b84fe9726f721e921b9623b sentientwaffle 1328999518 -0700 commit: 2 diff --git a/test/fixtures/branched/objects/17/2b99dd464f6c9c101a987991fcc63491f08985 b/test/fixtures/branched/objects/17/2b99dd464f6c9c101a987991fcc63491f08985 new file mode 100644 index 0000000000000000000000000000000000000000..49a6293730f8ff21e3399c1022d5c4e0e7b0e5ea GIT binary patch literal 85 zcmV-b0IL6Z0V^p=O;s?nU@$Z=Ff%bx2y%6F@paY9O<{QQKl5(C_|IW-PUz( z9tjU^H}koO21){xCaIK1HsDGsVWJ8V87!&Q=$>V_NAm(4Oj!oj2o;Dh$G{jPk4d5> z$tXk`TzR+qw)tg==+po8^%*#o)_2LJ@lyeS4@<$i}T&MkNv%e$`-50c$ FOF0C>Mgaf- literal 0 HcmV?d00001 diff --git a/test/fixtures/branched/objects/91/3318e66e9beed3e89e9c402c1d6585ef3f7e6f b/test/fixtures/branched/objects/91/3318e66e9beed3e89e9c402c1d6585ef3f7e6f new file mode 100644 index 0000000..5738805 --- /dev/null +++ b/test/fixtures/branched/objects/91/3318e66e9beed3e89e9c402c1d6585ef3f7e6f @@ -0,0 +1,2 @@ +x +1 D=+JfD6muue۫g0]e}lpYbjmDlSeO1J+m!#MU)[RƆ;3,.{I~_7hοz[8\Z_J;C7PxN \ No newline at end of file diff --git a/test/fixtures/branched/objects/9a/bc538b0dc8bf3974e04f3cb5d3f06240e8cef6 b/test/fixtures/branched/objects/9a/bc538b0dc8bf3974e04f3cb5d3f06240e8cef6 new file mode 100644 index 0000000000000000000000000000000000000000..0573a8744417340c1100fc6dd0728172a489fa14 GIT binary patch literal 54 zcmbF=Lop9e|fq+Ew|_7Ealo5 K76zBq0{#FeUKQ8? literal 0 HcmV?d00001 diff --git a/test/fixtures/branched/objects/b0/45ec4b14c60f03329654266478b6e8b92b33c5 b/test/fixtures/branched/objects/b0/45ec4b14c60f03329654266478b6e8b92b33c5 new file mode 100644 index 0000000000000000000000000000000000000000..c8d7334e9610cac24fda636c3e1f9f147c6c812d GIT binary patch literal 121 zcmV-<0EYi~0i}&G4gw(%MXfo-TtG7exIkizS7BgKl0}jgv)5l*Yl~mJSG=EkYjps5 zdm5Vv@Iq{z_EJn@%_a*m~Je b2Qu1+5Ntp=qeE2F&g&t6MHHz6A$UFs&9XD~D{Ff%bx2y%6F@paY9O<{QQKl5(C9VK6i>Ff%bx$jH\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/test/fixtures/checkout/hooks/post-commit.sample b/test/fixtures/checkout/hooks/post-commit.sample new file mode 100755 index 0000000..2266821 --- /dev/null +++ b/test/fixtures/checkout/hooks/post-commit.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script that is called after a successful +# commit is made. +# +# To enable this hook, rename this file to "post-commit". + +: Nothing diff --git a/test/fixtures/checkout/hooks/post-receive.sample b/test/fixtures/checkout/hooks/post-receive.sample new file mode 100755 index 0000000..7a83e17 --- /dev/null +++ b/test/fixtures/checkout/hooks/post-receive.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script for the "post-receive" event. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for a sample, or uncomment the next line and +# rename the file to "post-receive". + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/test/fixtures/checkout/hooks/post-update.sample b/test/fixtures/checkout/hooks/post-update.sample new file mode 100755 index 0000000..5323b56 --- /dev/null +++ b/test/fixtures/checkout/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git-update-server-info diff --git a/test/fixtures/checkout/hooks/pre-applypatch.sample b/test/fixtures/checkout/hooks/pre-applypatch.sample new file mode 100755 index 0000000..b1f187c --- /dev/null +++ b/test/fixtures/checkout/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: diff --git a/test/fixtures/checkout/hooks/pre-commit.sample b/test/fixtures/checkout/hooks/pre-commit.sample new file mode 100755 index 0000000..439eefd --- /dev/null +++ b/test/fixtures/checkout/hooks/pre-commit.sample @@ -0,0 +1,46 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by git-commit with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git-rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test "$(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0')" +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +exec git diff-index --check --cached $against -- diff --git a/test/fixtures/checkout/hooks/pre-rebase.sample b/test/fixtures/checkout/hooks/pre-rebase.sample new file mode 100755 index 0000000..be1b06e --- /dev/null +++ b/test/fixtures/checkout/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git-rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/test/fixtures/checkout/hooks/prepare-commit-msg.sample b/test/fixtures/checkout/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..3652424 --- /dev/null +++ b/test/fixtures/checkout/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/test/fixtures/checkout/hooks/update.sample b/test/fixtures/checkout/hooks/update.sample new file mode 100755 index 0000000..fd63b2d --- /dev/null +++ b/test/fixtures/checkout/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git-cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/test/fixtures/checkout/index b/test/fixtures/checkout/index new file mode 100644 index 0000000000000000000000000000000000000000..481437d04c22c5275301d2df44070d0cd98da059 GIT binary patch literal 104 zcmZ?q402{*U|<4bMt`gA)A*kRZH3Vc3>=IM57td$U}#*zz`* 1329248161 -0700 commit (initial): commit 1 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248175 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248189 -0700 checkout: moving from b2 to b3 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248244 -0700 checkout: moving from b3 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248321 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248321 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248322 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248322 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248323 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248323 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248473 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248473 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248474 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248474 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248475 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248475 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248476 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248476 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248477 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248477 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248601 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248601 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248847 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248847 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248858 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248858 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248860 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248860 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248862 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248862 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329249013 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329249013 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329249035 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329249035 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329249036 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329249036 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329251274 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329251274 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329251361 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329251362 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329251374 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329251374 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329251484 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329251484 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252221 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252221 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252236 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252236 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252258 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252258 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252268 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252268 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252292 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252292 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252335 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252335 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252659 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252659 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252671 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252671 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252678 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252678 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252700 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252700 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252712 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252712 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252716 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252716 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252723 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329252723 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253157 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253157 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253172 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253172 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253180 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253180 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253190 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253190 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253560 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329253560 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329255269 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329255269 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329257716 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329257716 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329257834 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329257834 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329257882 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329257882 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329257891 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329257891 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329258056 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329258056 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329271222 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329271222 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329271295 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329271295 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329274841 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329274841 -0700 checkout: moving from b2 to master +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329316383 -0700 checkout: moving from master to b2 +5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329316383 -0700 checkout: moving from b2 to master diff --git a/test/fixtures/checkout/logs/refs/heads/b2 b/test/fixtures/checkout/logs/refs/heads/b2 new file mode 100644 index 0000000..1f44f57 --- /dev/null +++ b/test/fixtures/checkout/logs/refs/heads/b2 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248175 -0700 branch: Created from HEAD diff --git a/test/fixtures/checkout/logs/refs/heads/b3 b/test/fixtures/checkout/logs/refs/heads/b3 new file mode 100644 index 0000000..fa6e33b --- /dev/null +++ b/test/fixtures/checkout/logs/refs/heads/b3 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248189 -0700 branch: Created from HEAD diff --git a/test/fixtures/checkout/logs/refs/heads/master b/test/fixtures/checkout/logs/refs/heads/master new file mode 100644 index 0000000..7f3a87a --- /dev/null +++ b/test/fixtures/checkout/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248161 -0700 commit (initial): commit 1 diff --git a/test/fixtures/checkout/objects/5d/0a23d3d71875e5abe637a1b7b3b9ea050f16aa b/test/fixtures/checkout/objects/5d/0a23d3d71875e5abe637a1b7b3b9ea050f16aa new file mode 100644 index 0000000000000000000000000000000000000000..dda8fee9fea79c35645105c39824d8b39b3f807b GIT binary patch literal 125 zcmV-@0D}K`0i}&Q4#F@H1*v_Cy#UC&@h6s0#8r&F3zlLmB}TbEE@-GJUNKY5ORd(@ z0Rx`;#tdvFqLV2I8=tLpboVA1u>mR1EZXOchN{z${v+oz*_=>N$tS--9r`^A+@P()U2NwqW HR>5}w;%5}l literal 0 HcmV?d00001 diff --git a/test/fixtures/checkout/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/test/fixtures/checkout/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 0000000000000000000000000000000000000000..711223894375fe1186ac5bfffdc48fb1fa1e65cc GIT binary patch literal 15 Wcmb\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/test/fixtures/remotes/hooks/post-commit.sample b/test/fixtures/remotes/hooks/post-commit.sample new file mode 100755 index 0000000..2266821 --- /dev/null +++ b/test/fixtures/remotes/hooks/post-commit.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script that is called after a successful +# commit is made. +# +# To enable this hook, rename this file to "post-commit". + +: Nothing diff --git a/test/fixtures/remotes/hooks/post-receive.sample b/test/fixtures/remotes/hooks/post-receive.sample new file mode 100755 index 0000000..7a83e17 --- /dev/null +++ b/test/fixtures/remotes/hooks/post-receive.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script for the "post-receive" event. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for a sample, or uncomment the next line and +# rename the file to "post-receive". + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/test/fixtures/remotes/hooks/post-update.sample b/test/fixtures/remotes/hooks/post-update.sample new file mode 100755 index 0000000..5323b56 --- /dev/null +++ b/test/fixtures/remotes/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git-update-server-info diff --git a/test/fixtures/remotes/hooks/pre-applypatch.sample b/test/fixtures/remotes/hooks/pre-applypatch.sample new file mode 100755 index 0000000..b1f187c --- /dev/null +++ b/test/fixtures/remotes/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: diff --git a/test/fixtures/remotes/hooks/pre-commit.sample b/test/fixtures/remotes/hooks/pre-commit.sample new file mode 100755 index 0000000..439eefd --- /dev/null +++ b/test/fixtures/remotes/hooks/pre-commit.sample @@ -0,0 +1,46 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by git-commit with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git-rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test "$(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0')" +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +exec git diff-index --check --cached $against -- diff --git a/test/fixtures/remotes/hooks/pre-rebase.sample b/test/fixtures/remotes/hooks/pre-rebase.sample new file mode 100755 index 0000000..be1b06e --- /dev/null +++ b/test/fixtures/remotes/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git-rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/test/fixtures/remotes/hooks/prepare-commit-msg.sample b/test/fixtures/remotes/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..3652424 --- /dev/null +++ b/test/fixtures/remotes/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/test/fixtures/remotes/hooks/update.sample b/test/fixtures/remotes/hooks/update.sample new file mode 100755 index 0000000..fd63b2d --- /dev/null +++ b/test/fixtures/remotes/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git-cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/test/fixtures/remotes/index b/test/fixtures/remotes/index new file mode 100644 index 0000000000000000000000000000000000000000..cd97037a61a4c34a04f3bf111bd54a62ca6c7cc0 GIT binary patch literal 264 zcmZ?q402{*U|<4bW`B$79>rrZQZSlv$E(u_K`1kF?zRR%F``T q3|yIcDXA5D86~+n5YreK{)r!uES^&Tzq;+D{_ZbZ^qbyR7y|%DUsm$~ literal 0 HcmV?d00001 diff --git a/test/fixtures/remotes/info/exclude b/test/fixtures/remotes/info/exclude new file mode 100644 index 0000000..2c87b72 --- /dev/null +++ b/test/fixtures/remotes/info/exclude @@ -0,0 +1,6 @@ +# git-ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/test/fixtures/remotes/logs/HEAD b/test/fixtures/remotes/logs/HEAD new file mode 100644 index 0000000..69db901 --- /dev/null +++ b/test/fixtures/remotes/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 bdd3996d38d885e18e5c5960df1c2c06e34d673f sentientwaffle 1329101708 -0700 clone: from git@github.com:octocat/Spoon-Knife.git diff --git a/test/fixtures/remotes/logs/refs/heads/master b/test/fixtures/remotes/logs/refs/heads/master new file mode 100644 index 0000000..69db901 --- /dev/null +++ b/test/fixtures/remotes/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 bdd3996d38d885e18e5c5960df1c2c06e34d673f sentientwaffle 1329101708 -0700 clone: from git@github.com:octocat/Spoon-Knife.git diff --git a/test/fixtures/remotes/objects/pack/pack-3f24024466408b565d66ecd5902c84ec2362e513.idx b/test/fixtures/remotes/objects/pack/pack-3f24024466408b565d66ecd5902c84ec2362e513.idx new file mode 100644 index 0000000000000000000000000000000000000000..7041e42b983809e6a5c83a91a2b2e73e46d814e0 GIT binary patch literal 1744 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\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/test/fixtures/simple/hooks/post-commit.sample b/test/fixtures/simple/hooks/post-commit.sample new file mode 100755 index 0000000..2266821 --- /dev/null +++ b/test/fixtures/simple/hooks/post-commit.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script that is called after a successful +# commit is made. +# +# To enable this hook, rename this file to "post-commit". + +: Nothing diff --git a/test/fixtures/simple/hooks/post-receive.sample b/test/fixtures/simple/hooks/post-receive.sample new file mode 100755 index 0000000..7a83e17 --- /dev/null +++ b/test/fixtures/simple/hooks/post-receive.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script for the "post-receive" event. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for a sample, or uncomment the next line and +# rename the file to "post-receive". + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/test/fixtures/simple/hooks/post-update.sample b/test/fixtures/simple/hooks/post-update.sample new file mode 100755 index 0000000..5323b56 --- /dev/null +++ b/test/fixtures/simple/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git-update-server-info diff --git a/test/fixtures/simple/hooks/pre-applypatch.sample b/test/fixtures/simple/hooks/pre-applypatch.sample new file mode 100755 index 0000000..b1f187c --- /dev/null +++ b/test/fixtures/simple/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: diff --git a/test/fixtures/simple/hooks/pre-commit.sample b/test/fixtures/simple/hooks/pre-commit.sample new file mode 100755 index 0000000..439eefd --- /dev/null +++ b/test/fixtures/simple/hooks/pre-commit.sample @@ -0,0 +1,46 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by git-commit with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git-rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test "$(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0')" +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +exec git diff-index --check --cached $against -- diff --git a/test/fixtures/simple/hooks/pre-rebase.sample b/test/fixtures/simple/hooks/pre-rebase.sample new file mode 100755 index 0000000..be1b06e --- /dev/null +++ b/test/fixtures/simple/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git-rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/test/fixtures/simple/hooks/prepare-commit-msg.sample b/test/fixtures/simple/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..3652424 --- /dev/null +++ b/test/fixtures/simple/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/test/fixtures/simple/hooks/update.sample b/test/fixtures/simple/hooks/update.sample new file mode 100755 index 0000000..fd63b2d --- /dev/null +++ b/test/fixtures/simple/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git-cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/test/fixtures/simple/index b/test/fixtures/simple/index new file mode 100644 index 0000000000000000000000000000000000000000..3330d716f1541c5acb1ed77867020cc7d88df5b7 GIT binary patch literal 32 mcmZ?q402{*U|<4b2Fn{0gy%gq&8hoZq?q&itoka)pZ5Wd4h#_h literal 0 HcmV?d00001 diff --git a/test/fixtures/simple/info/exclude b/test/fixtures/simple/info/exclude new file mode 100644 index 0000000..2c87b72 --- /dev/null +++ b/test/fixtures/simple/info/exclude @@ -0,0 +1,6 @@ +# git-ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/test/fixtures/status/COMMIT_EDITMSG b/test/fixtures/status/COMMIT_EDITMSG new file mode 100644 index 0000000..e79c5e8 --- /dev/null +++ b/test/fixtures/status/COMMIT_EDITMSG @@ -0,0 +1 @@ +initial diff --git a/test/fixtures/status/HEAD b/test/fixtures/status/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/test/fixtures/status/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/test/fixtures/status/config b/test/fixtures/status/config new file mode 100644 index 0000000..515f483 --- /dev/null +++ b/test/fixtures/status/config @@ -0,0 +1,5 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true diff --git a/test/fixtures/status/description b/test/fixtures/status/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/test/fixtures/status/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/test/fixtures/status/hooks/applypatch-msg.sample b/test/fixtures/status/hooks/applypatch-msg.sample new file mode 100755 index 0000000..8b2a2fe --- /dev/null +++ b/test/fixtures/status/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +test -x "$GIT_DIR/hooks/commit-msg" && + exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +: diff --git a/test/fixtures/status/hooks/commit-msg.sample b/test/fixtures/status/hooks/commit-msg.sample new file mode 100755 index 0000000..6ef1d29 --- /dev/null +++ b/test/fixtures/status/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by git-commit with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/test/fixtures/status/hooks/post-commit.sample b/test/fixtures/status/hooks/post-commit.sample new file mode 100755 index 0000000..2266821 --- /dev/null +++ b/test/fixtures/status/hooks/post-commit.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script that is called after a successful +# commit is made. +# +# To enable this hook, rename this file to "post-commit". + +: Nothing diff --git a/test/fixtures/status/hooks/post-receive.sample b/test/fixtures/status/hooks/post-receive.sample new file mode 100755 index 0000000..7a83e17 --- /dev/null +++ b/test/fixtures/status/hooks/post-receive.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script for the "post-receive" event. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for a sample, or uncomment the next line and +# rename the file to "post-receive". + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/test/fixtures/status/hooks/post-update.sample b/test/fixtures/status/hooks/post-update.sample new file mode 100755 index 0000000..5323b56 --- /dev/null +++ b/test/fixtures/status/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git-update-server-info diff --git a/test/fixtures/status/hooks/pre-applypatch.sample b/test/fixtures/status/hooks/pre-applypatch.sample new file mode 100755 index 0000000..b1f187c --- /dev/null +++ b/test/fixtures/status/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: diff --git a/test/fixtures/status/hooks/pre-commit.sample b/test/fixtures/status/hooks/pre-commit.sample new file mode 100755 index 0000000..439eefd --- /dev/null +++ b/test/fixtures/status/hooks/pre-commit.sample @@ -0,0 +1,46 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by git-commit with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git-rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test "$(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0')" +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +exec git diff-index --check --cached $against -- diff --git a/test/fixtures/status/hooks/pre-rebase.sample b/test/fixtures/status/hooks/pre-rebase.sample new file mode 100755 index 0000000..be1b06e --- /dev/null +++ b/test/fixtures/status/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git-rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/test/fixtures/status/hooks/prepare-commit-msg.sample b/test/fixtures/status/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..3652424 --- /dev/null +++ b/test/fixtures/status/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/test/fixtures/status/hooks/update.sample b/test/fixtures/status/hooks/update.sample new file mode 100755 index 0000000..fd63b2d --- /dev/null +++ b/test/fixtures/status/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git-cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/test/fixtures/status/index b/test/fixtures/status/index new file mode 100644 index 0000000000000000000000000000000000000000..8463377e2d9719f474af9f7a8c5301215bb58b2a GIT binary patch literal 184 zcmZ?q402{*U|<4af6M8qCJxyqFq(mZgOTCEx@8OujY}99m|s9J1B)b=1anQw5xZEwO}uH=l=)Z$dVl8O?bSzzFAIrA9zj_gbr4K;5mnt7~i_O%^9F}3Z% tL@lmX={27@k4` 1329174405 -0700 commit (initial): initial diff --git a/test/fixtures/status/logs/refs/heads/master b/test/fixtures/status/logs/refs/heads/master new file mode 100644 index 0000000..a0a59dc --- /dev/null +++ b/test/fixtures/status/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 10c14fdc111b6776b1fc3740e8b16939233f7a84 sentientwaffle 1329174405 -0700 commit (initial): initial diff --git a/test/fixtures/status/objects/10/c14fdc111b6776b1fc3740e8b16939233f7a84 b/test/fixtures/status/objects/10/c14fdc111b6776b1fc3740e8b16939233f7a84 new file mode 100644 index 0000000000000000000000000000000000000000..55727a85189e778159787522e13388f2b2190fe2 GIT binary patch literal 126 zcmV-^0D=E_0i}&g4gw(%1zmFrE}-d#8JbCq@hZ(vNnl9EHeO%0u3fxE)uLV;y;sq= z!AXvsb@IWl9Ew5b$;8B#f}`XMM3azRVUJAFDt^mz9Qu`asdFDLrDc8n<@=*^ZI?Lu gO`|m%(z3v+&p-fb+IcPcuZXI9mC8+h0mgVh<(`8++yDRo literal 0 HcmV?d00001 diff --git a/test/fixtures/status/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf b/test/fixtures/status/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf new file mode 100644 index 0000000000000000000000000000000000000000..cd18ee64418a33c7a2a2aa1ee0d2892eb95c2d4b GIT binary patch literal 19 acmbP7Mbv literal 0 HcmV?d00001 diff --git a/test/fixtures/status/objects/d8/b85abf216872cda37b67d8f0c2d1a6fa81a7c3 b/test/fixtures/status/objects/d8/b85abf216872cda37b67d8f0c2d1a6fa81a7c3 new file mode 100644 index 0000000000000000000000000000000000000000..0fa29f836ed661d987b50f5828a7251dd2dfafbb GIT binary patch literal 116 zcmV-)0E_>40V^p=O;s>7G+{6_FfcPQQAo~6O)XB\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/test/fixtures/submodule/hooks/post-commit.sample b/test/fixtures/submodule/hooks/post-commit.sample new file mode 100755 index 0000000..2266821 --- /dev/null +++ b/test/fixtures/submodule/hooks/post-commit.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script that is called after a successful +# commit is made. +# +# To enable this hook, rename this file to "post-commit". + +: Nothing diff --git a/test/fixtures/submodule/hooks/post-receive.sample b/test/fixtures/submodule/hooks/post-receive.sample new file mode 100755 index 0000000..7a83e17 --- /dev/null +++ b/test/fixtures/submodule/hooks/post-receive.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script for the "post-receive" event. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for a sample, or uncomment the next line and +# rename the file to "post-receive". + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/test/fixtures/submodule/hooks/post-update.sample b/test/fixtures/submodule/hooks/post-update.sample new file mode 100755 index 0000000..5323b56 --- /dev/null +++ b/test/fixtures/submodule/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git-update-server-info diff --git a/test/fixtures/submodule/hooks/pre-applypatch.sample b/test/fixtures/submodule/hooks/pre-applypatch.sample new file mode 100755 index 0000000..b1f187c --- /dev/null +++ b/test/fixtures/submodule/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: diff --git a/test/fixtures/submodule/hooks/pre-commit.sample b/test/fixtures/submodule/hooks/pre-commit.sample new file mode 100755 index 0000000..439eefd --- /dev/null +++ b/test/fixtures/submodule/hooks/pre-commit.sample @@ -0,0 +1,46 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by git-commit with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git-rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test "$(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0')" +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +exec git diff-index --check --cached $against -- diff --git a/test/fixtures/submodule/hooks/pre-rebase.sample b/test/fixtures/submodule/hooks/pre-rebase.sample new file mode 100755 index 0000000..be1b06e --- /dev/null +++ b/test/fixtures/submodule/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git-rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/test/fixtures/submodule/hooks/prepare-commit-msg.sample b/test/fixtures/submodule/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..3652424 --- /dev/null +++ b/test/fixtures/submodule/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/test/fixtures/submodule/hooks/update.sample b/test/fixtures/submodule/hooks/update.sample new file mode 100755 index 0000000..fd63b2d --- /dev/null +++ b/test/fixtures/submodule/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git-cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/test/fixtures/submodule/index b/test/fixtures/submodule/index new file mode 100644 index 0000000000000000000000000000000000000000..9080cda5ca869d9dbfb3f4bf764d045fbf53e001 GIT binary patch literal 264 zcmZ?q402{*U|<4bW`C<=erAi>Z^38=1`bAs2kRLb7#f!VrC&fWL!5P({b9Sx$?1tV#vvn2+jE0!E?gE;5%&t3|_nB?C zVehyzXV3PW-3@=LH-s~=Wn}7=RFpu>!Q!rm2|&3AKxvq}1Q_;So|$WLqxE55Ok~1+ x86CFAzUlT1+{Fd?`FXn8d6{XcAj5#b>xP)CJ(B@j|7`WH`75sFOPznr3IJ=6SWW-{ literal 0 HcmV?d00001 diff --git a/test/fixtures/submodule/info/exclude b/test/fixtures/submodule/info/exclude new file mode 100644 index 0000000..2c87b72 --- /dev/null +++ b/test/fixtures/submodule/info/exclude @@ -0,0 +1,6 @@ +# git-ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/test/fixtures/submodule/logs/HEAD b/test/fixtures/submodule/logs/HEAD new file mode 100644 index 0000000..ce6d6a7 --- /dev/null +++ b/test/fixtures/submodule/logs/HEAD @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 ea4ab982469a4a539b773a091670224343db1eee sentientwaffle 1329251828 -0700 commit (initial): initial +ea4ab982469a4a539b773a091670224343db1eee fbeb68ceaa10264b36573022d784137bab4fa273 sentientwaffle 1329252042 -0700 commit: add a submodule diff --git a/test/fixtures/submodule/logs/refs/heads/master b/test/fixtures/submodule/logs/refs/heads/master new file mode 100644 index 0000000..ce6d6a7 --- /dev/null +++ b/test/fixtures/submodule/logs/refs/heads/master @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 ea4ab982469a4a539b773a091670224343db1eee sentientwaffle 1329251828 -0700 commit (initial): initial +ea4ab982469a4a539b773a091670224343db1eee fbeb68ceaa10264b36573022d784137bab4fa273 sentientwaffle 1329252042 -0700 commit: add a submodule diff --git a/test/fixtures/submodule/objects/3b/563fc33e799367e2d2824fd21e0219fc7591db b/test/fixtures/submodule/objects/3b/563fc33e799367e2d2824fd21e0219fc7591db new file mode 100644 index 0000000000000000000000000000000000000000..51d43835fa9fd0c5718ed75884ee3431a358c7b3 GIT binary patch literal 92 zcmV-i0HgnS0ZYosPg1ZnVTdj+P0GzrDa}b$P%19S&(G7%&dW?oRf^@}EJ!TLP_R`% y66NA7Ey@83rDv8{>FWc@jM5~%7G-5C`FfcPQQP4}zEXmDJDa}bOX0Q&kKWtYyIsMV4CjU!v zOp<>}C*FoC%gEF#sVHG^-Pyd)Y`YD6$DKKQw&(0__*19VK6i>Ff%bx$jHM\eN,.Dh=)Db!07o 8R< 7Y[FҳGcu6G-"lZ>\*pGeVo!3th.(#֖U(P? \ No newline at end of file diff --git a/test/fixtures/submodule/refs/heads/master b/test/fixtures/submodule/refs/heads/master new file mode 100644 index 0000000..55d51c3 --- /dev/null +++ b/test/fixtures/submodule/refs/heads/master @@ -0,0 +1 @@ +fbeb68ceaa10264b36573022d784137bab4fa273 diff --git a/test/fixtures/tagged/COMMIT_EDITMSG b/test/fixtures/tagged/COMMIT_EDITMSG new file mode 100644 index 0000000..e5deda6 --- /dev/null +++ b/test/fixtures/tagged/COMMIT_EDITMSG @@ -0,0 +1 @@ +commit 6 diff --git a/test/fixtures/tagged/HEAD b/test/fixtures/tagged/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/test/fixtures/tagged/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/test/fixtures/tagged/config b/test/fixtures/tagged/config new file mode 100644 index 0000000..515f483 --- /dev/null +++ b/test/fixtures/tagged/config @@ -0,0 +1,5 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true diff --git a/test/fixtures/tagged/description b/test/fixtures/tagged/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/test/fixtures/tagged/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/test/fixtures/tagged/hooks/applypatch-msg.sample b/test/fixtures/tagged/hooks/applypatch-msg.sample new file mode 100755 index 0000000..8b2a2fe --- /dev/null +++ b/test/fixtures/tagged/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +test -x "$GIT_DIR/hooks/commit-msg" && + exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +: diff --git a/test/fixtures/tagged/hooks/commit-msg.sample b/test/fixtures/tagged/hooks/commit-msg.sample new file mode 100755 index 0000000..6ef1d29 --- /dev/null +++ b/test/fixtures/tagged/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by git-commit with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/test/fixtures/tagged/hooks/post-commit.sample b/test/fixtures/tagged/hooks/post-commit.sample new file mode 100755 index 0000000..2266821 --- /dev/null +++ b/test/fixtures/tagged/hooks/post-commit.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script that is called after a successful +# commit is made. +# +# To enable this hook, rename this file to "post-commit". + +: Nothing diff --git a/test/fixtures/tagged/hooks/post-receive.sample b/test/fixtures/tagged/hooks/post-receive.sample new file mode 100755 index 0000000..7a83e17 --- /dev/null +++ b/test/fixtures/tagged/hooks/post-receive.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script for the "post-receive" event. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for a sample, or uncomment the next line and +# rename the file to "post-receive". + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/test/fixtures/tagged/hooks/post-update.sample b/test/fixtures/tagged/hooks/post-update.sample new file mode 100755 index 0000000..5323b56 --- /dev/null +++ b/test/fixtures/tagged/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git-update-server-info diff --git a/test/fixtures/tagged/hooks/pre-applypatch.sample b/test/fixtures/tagged/hooks/pre-applypatch.sample new file mode 100755 index 0000000..b1f187c --- /dev/null +++ b/test/fixtures/tagged/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: diff --git a/test/fixtures/tagged/hooks/pre-commit.sample b/test/fixtures/tagged/hooks/pre-commit.sample new file mode 100755 index 0000000..439eefd --- /dev/null +++ b/test/fixtures/tagged/hooks/pre-commit.sample @@ -0,0 +1,46 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by git-commit with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git-rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test "$(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0')" +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +exec git diff-index --check --cached $against -- diff --git a/test/fixtures/tagged/hooks/pre-rebase.sample b/test/fixtures/tagged/hooks/pre-rebase.sample new file mode 100755 index 0000000..be1b06e --- /dev/null +++ b/test/fixtures/tagged/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git-rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/test/fixtures/tagged/hooks/prepare-commit-msg.sample b/test/fixtures/tagged/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..3652424 --- /dev/null +++ b/test/fixtures/tagged/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/test/fixtures/tagged/hooks/update.sample b/test/fixtures/tagged/hooks/update.sample new file mode 100755 index 0000000..fd63b2d --- /dev/null +++ b/test/fixtures/tagged/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git-cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/test/fixtures/tagged/index b/test/fixtures/tagged/index new file mode 100644 index 0000000000000000000000000000000000000000..5abe75a0754f772fc8c0ae0af8dacaf98430fe2c GIT binary patch literal 104 zcmZ?q402{*U|<4bMt=+cBr~pv9Wa`KfrF9Z!MZ30hQ=if49qVen1Ox1X_wdC_op^Z x*;~hIjMRj6(tM|6V=YB@V>KrQFm}!rcrjQP(Xs9DFEQYAv*v7 literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/info/exclude b/test/fixtures/tagged/info/exclude new file mode 100644 index 0000000..2c87b72 --- /dev/null +++ b/test/fixtures/tagged/info/exclude @@ -0,0 +1,6 @@ +# git-ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/test/fixtures/tagged/logs/HEAD b/test/fixtures/tagged/logs/HEAD new file mode 100644 index 0000000..0f69b14 --- /dev/null +++ b/test/fixtures/tagged/logs/HEAD @@ -0,0 +1,6 @@ +0000000000000000000000000000000000000000 91d6d39cd8638f1067989e0ab9c9c00ca378c952 sentientwaffle 1329090329 -0700 commit (initial): commit 1 +91d6d39cd8638f1067989e0ab9c9c00ca378c952 ebcbdf47e0ad6503d09f59cd8eeb76d9435a0396 sentientwaffle 1329090345 -0700 commit: commit 2 +ebcbdf47e0ad6503d09f59cd8eeb76d9435a0396 ea1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c sentientwaffle 1329090351 -0700 commit: commit 3 +ea1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c bbfb0e8e077ab9b37941097883f4e917ecca3d33 sentientwaffle 1329090358 -0700 commit: commit 4 +bbfb0e8e077ab9b37941097883f4e917ecca3d33 32bbb351de16c3e404b3b7c77601c3d124e1e1a1 sentientwaffle 1329090365 -0700 commit: commit 5 +32bbb351de16c3e404b3b7c77601c3d124e1e1a1 78cb34985ee281f9b8008b556ad5b4dd36473b10 sentientwaffle 1329090407 -0700 commit: commit 6 diff --git a/test/fixtures/tagged/logs/refs/heads/master b/test/fixtures/tagged/logs/refs/heads/master new file mode 100644 index 0000000..0f69b14 --- /dev/null +++ b/test/fixtures/tagged/logs/refs/heads/master @@ -0,0 +1,6 @@ +0000000000000000000000000000000000000000 91d6d39cd8638f1067989e0ab9c9c00ca378c952 sentientwaffle 1329090329 -0700 commit (initial): commit 1 +91d6d39cd8638f1067989e0ab9c9c00ca378c952 ebcbdf47e0ad6503d09f59cd8eeb76d9435a0396 sentientwaffle 1329090345 -0700 commit: commit 2 +ebcbdf47e0ad6503d09f59cd8eeb76d9435a0396 ea1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c sentientwaffle 1329090351 -0700 commit: commit 3 +ea1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c bbfb0e8e077ab9b37941097883f4e917ecca3d33 sentientwaffle 1329090358 -0700 commit: commit 4 +bbfb0e8e077ab9b37941097883f4e917ecca3d33 32bbb351de16c3e404b3b7c77601c3d124e1e1a1 sentientwaffle 1329090365 -0700 commit: commit 5 +32bbb351de16c3e404b3b7c77601c3d124e1e1a1 78cb34985ee281f9b8008b556ad5b4dd36473b10 sentientwaffle 1329090407 -0700 commit: commit 6 diff --git a/test/fixtures/tagged/objects/12/d411f1066eccc420e1f0361ecf88f8e2b65f5b b/test/fixtures/tagged/objects/12/d411f1066eccc420e1f0361ecf88f8e2b65f5b new file mode 100644 index 0000000000000000000000000000000000000000..3fd7e582a421465e42b3693b0899f582e1208658 GIT binary patch literal 53 zcmb9V=y!@Ff%bxNXyJg)hnqeVUXmKV6I6yVwWxGG55=U?s?lT Lo!k!q9`FyO3wRbJ literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/objects/32/bbb351de16c3e404b3b7c77601c3d124e1e1a1 b/test/fixtures/tagged/objects/32/bbb351de16c3e404b3b7c77601c3d124e1e1a1 new file mode 100644 index 0000000000000000000000000000000000000000..ea26a0bf5a5bfaf3d17eba53987914908736460b GIT binary patch literal 153 zcmV;K0A~Mq0i}*R4gxU@M5%KMUjSss`AP_Jl_YD!N-T?(Ag)ge8fuCuW~6yDZC#eR zgXO&In+TAd9FY=+AgwiLQAm*kNz;@xiLJq_ujWxUxp$~lD@qcKvD92SW=EJ~N~}&Y z#Q`ZiF`M%0*L8#ED9uCfqIwfJ{`&J}Df8{n*5w4mHe(*s9`+a!%}B@9E`LG36!7K) H6{=2JhDb%9 literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/objects/41/0a16650aa2a129ed639f66b7ce17bd76b90e65 b/test/fixtures/tagged/objects/41/0a16650aa2a129ed639f66b7ce17bd76b90e65 new file mode 100644 index 0000000000000000000000000000000000000000..76c9181fabe79882fbfde4adb9bbd18f875649cc GIT binary patch literal 53 zcmV-50LuS(0V^p=O;s>9V=y!@Ff%bxNXyJg)hnqeVesJ4FZ$4at}cIh^c=_Q7Pafn Lx>N%IIo}X}E2S4d literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/objects/48/082f72f087ce7e6fa75b9c41d7387daecd447b b/test/fixtures/tagged/objects/48/082f72f087ce7e6fa75b9c41d7387daecd447b new file mode 100644 index 0000000000000000000000000000000000000000..23ff2840ff2459a64132f323f24859beb43d2125 GIT binary patch literal 18 Zcmbz~m7VgTMr)1OPqU1=#=q literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/objects/68/ba9551aa06079093a6b868b7fac98ed6634ec0 b/test/fixtures/tagged/objects/68/ba9551aa06079093a6b868b7fac98ed6634ec0 new file mode 100644 index 0000000000000000000000000000000000000000..cb06ed4d4530d15f750cbec95a344ad2287ca191 GIT binary patch literal 53 zcmV-50LuS(0V^p=O;s>9V=y!@Ff%bxNXyJg)hnqeVYt9D@o!!9wqF`Y#TV>0+Od@_ Lc&;x1O6L&c3M>`t literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/objects/78/cb34985ee281f9b8008b556ad5b4dd36473b10 b/test/fixtures/tagged/objects/78/cb34985ee281f9b8008b556ad5b4dd36473b10 new file mode 100644 index 0000000000000000000000000000000000000000..0be8efc4aada247f07bd77fc492298e0d4c1c084 GIT binary patch literal 154 zcmV;L0A>Gp0i}*X4#F@D1Ucsw{s731ou-ix;ww(-KqX313F70EGW|0i}&G4gxU@1gY~1e*k0?E{P?C_=+z$5a~jsyCA-g3mR&QU1_J7&Aqic zfS7h;RfUjd;b|ff3>X>NOPG_VFrbBDkV?=aD?Rw~6lCK5GeE^TsKq{MYI9LDx literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/objects/9f/358a4addefcab294b83e4282bfef1f9625a249 b/test/fixtures/tagged/objects/9f/358a4addefcab294b83e4282bfef1f9625a249 new file mode 100644 index 0000000000000000000000000000000000000000..d427286f97f674171ea7f71adc270e3890163cad GIT binary patch literal 22 dcmb_ˡmIAk5q_# OI \ No newline at end of file diff --git a/test/fixtures/tagged/objects/d0/0491fd7e5bb6fa28c517a0bb32b8b506539d4d b/test/fixtures/tagged/objects/d0/0491fd7e5bb6fa28c517a0bb32b8b506539d4d new file mode 100644 index 0000000000000000000000000000000000000000..8dab6a9eaf1fff6a519dfb7bec1a5bdb56ff845d GIT binary patch literal 17 Ycmb9V=y!@Ff%bxNXyJg)hnqeVQ4(+`u_FPueCm_wO78;^zr6d L`>hB7SsoG#K35m? literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/objects/e5/6e15bb7ddb6bd0b6d924b18fcee53d8713d7ea b/test/fixtures/tagged/objects/e5/6e15bb7ddb6bd0b6d924b18fcee53d8713d7ea new file mode 100644 index 0000000000000000000000000000000000000000..c866b476fbd73472a7487e500598efd5bcc5b80a GIT binary patch literal 21 ccmbT#KA;(38Fmt5{K_y4x{0`E#rSF`5T&dheE8!4gQyjA5=0Fwsm3oJmLk zghQHzqBs2N*LAbc0WBkMuJxw&_}kBy#>?#>YdcvNJOM$NqTNFRFk>B8yZ$BlTC6u8 Ha9K@uT2V!c literal 0 HcmV?d00001 diff --git a/test/fixtures/tagged/objects/f8/44a55bbc98af479703c35aeb2837cf0e4eff1a b/test/fixtures/tagged/objects/f8/44a55bbc98af479703c35aeb2837cf0e4eff1a new file mode 100644 index 0000000000000000000000000000000000000000..c7ca752d8242fc5109aa2c044d1b356990506c02 GIT binary patch literal 53 zcmb + describe "()", -> + repo = git "#{__dirname}/fixtures/simple" + + it "returns a Repo", -> + repo.should.be.an.instanceof Repo diff --git a/test/ref.test.coffee b/test/ref.test.coffee new file mode 100644 index 0000000..02242a3 --- /dev/null +++ b/test/ref.test.coffee @@ -0,0 +1,64 @@ +_ = require 'underscore' +should = require 'should' +fixtures = require './fixtures' +git = require '../src' +Commit = require '../src/commit' + +{Ref, Head} = require '../src/ref' + +describe "Ref", -> + describe ".find_all", -> + describe "find remotes", -> + repo = fixtures.remotes + remotes = null + before (done) -> + Ref.find_all repo, "remote", Ref, (err, _remotes) -> + remotes = _remotes + done err + + it "is an Array of Refs", -> + remotes.should.be.an.instanceof Array + remotes[0].should.be.an.instanceof Ref + + it "the first item is a remote", -> + remotes[0].name.should.eql "origin/HEAD" + remotes[0].commit.should.be.an.instanceof Commit + + it "the second item is a remote", -> + remotes[1].name.should.eql "origin/master" + remotes[1].commit.should.be.an.instanceof Commit + + +describe "Head", -> + describe ".find_all", -> + repo = fixtures.branched + heads = null + before (done) -> + Head.find_all repo, (err, h) -> + heads = h + done err + + it "is an Array of Heads", -> + heads.should.be.an.instanceof Array + heads[0].should.be.an.instanceof Head + + it "contains the branches", -> + heads.should.have.lengthOf 2 + names = _.map heads, ((b) -> b.name) + names.should.include "master" + names.should.include "something" + + + describe ".current", -> + repo = fixtures.branched + branch = null + before (done) -> + Head.current repo, (err, b) -> + branch = b + done err + + it "is a Head", -> + branch.should.be.an.instanceof Head + + it "has the correct name", -> + branch.name.should.eql "master" diff --git a/test/repo.test.coffee b/test/repo.test.coffee new file mode 100644 index 0000000..6295488 --- /dev/null +++ b/test/repo.test.coffee @@ -0,0 +1,271 @@ +should = require 'should' +fixtures = require './fixtures' +git = require '../src' +Commit = require '../src/commit' +Tree = require '../src/tree' +Diff = require '../src/diff' +Tag = require '../src/tag' +Status = require '../src/status' + +{Ref, Head} = require '../src/ref' + +describe "Repo", -> + describe "#commits", -> + describe "with only a callback", -> + repo = fixtures.branched + commits = null + before (done) -> + repo.commits (err, _commits) -> + commits = _commits + done err + + it "passes an Array", -> + commits.should.be.an.instanceof Array + + it "is a list of commits", -> + commits[0].id.should.eql "913318e66e9beed3e89e9c402c1d6585ef3f7e6f" + commits[0].repo.should.eql repo + commits[0].author.name.should.eql "sentientwaffle" + commits[0].committer.name.should.eql "sentientwaffle" + commits[0].authored_date.should.be.an.instanceof Date + commits[0].committed_date.should.be.an.instanceof Date + commits[0].parents().should.be.an.instanceof Array + commits[0].message.should.eql "add a sub dir" + + describe "specify a branch", -> + repo = fixtures.branched + commits = null + before (done) -> + repo.commits "something", (err, _commits) -> + commits = _commits + done err + + # The first commit ... + it "is the latest commit", -> + commits[0].message.should.eql "2" + + it "has a parent commit", -> + commits[0].parents().should.have.lengthOf 1 + commits[0].parents()[0].id.should.eql commits[1].id + + describe "specify a tag", -> + repo = fixtures.tagged + commits = null + before (done) -> + repo.commits "tag-1", (err, _commits) -> + commits = _commits + done err + + it "is the latest commit on the tag", -> + commits[0].message.should.include "commit 5" + + describe "limit the number of commits", -> + repo = fixtures.tagged + commits = null + before (done) -> + repo.commits "master", 2, (err, _commits) -> + commits = _commits + done err + + it "returns 2 commits", -> + commits.should.have.lengthOf 2 + + describe "skip commits", -> + repo = fixtures.tagged + commits = null + before (done) -> + repo.commits "master", 1, 2, (err, _commits) -> + commits = _commits + done err + + it "returns 2 commits", -> + commits[0].message.should.include "commit 4" + + + describe "#tree", -> + repo = fixtures.branched + describe "master", -> + it "is a Tree", -> + repo.tree().should.be.an.instanceof Tree + + it "checks out branch:master", (done) -> + repo.tree().blobs (err, blobs) -> + blobs[0].data (err, data) -> + data.should.include "Bla" + data.should.not.include "Bla2" + done err + + describe "specific branch", -> + it "is a Tree", -> + repo.tree("something").should.be.an.instanceof Tree + + it "checks out branch:something", (done) -> + repo.tree("something").blobs (err, blobs) -> + blobs[0].data (err, data) -> + data.should.include "Bla2" + done err + + + describe "#diff", -> + repo = fixtures.branched + describe "between 2 branches", -> + diffs = null + before (done) -> + repo.diff "something", "master", (err, _diffs) -> + diffs = _diffs + done err + + it "is passes an Array of Diffs", -> + diffs.should.be.an.instanceof Array + diffs[0].should.be.an.instanceof Diff + + # The first diff... + it "modifies the README.md file", -> + diffs[0].a_path.should.eql "README.md" + diffs[0].b_path.should.eql "README.md" + + # The second diff... + it "creates some/hi.txt", -> + diffs[1].new_file.should.be.true + diffs[1].b_path.should.eql "some/hi.txt" + + + describe "#remotes", -> + describe "in a repository with remotes", -> + repo = fixtures.remotes + remotes = null + before (done) -> + repo.remotes (err, _remotes) -> + remotes = _remotes + done err + + it "is an Array of Refs", -> + remotes.should.be.an.instanceof Array + remotes[0].should.be.an.instanceof Ref + + it "contains the correct Refs", -> + remotes[0].commit.id.should.eql "bdd3996d38d885e18e5c5960df1c2c06e34d673f" + remotes[0].name.should.eql "origin/HEAD" + remotes[1].commit.id.should.eql "bdd3996d38d885e18e5c5960df1c2c06e34d673f" + remotes[1].name.should.eql "origin/master" + + describe "when there are no remotes", -> + repo = fixtures.branched + it "is an empty Array", -> + repo.remotes (err, remotes) -> + remotes.should.eql [] + + + describe "#remote_list", -> + describe "in a repository with remotes", -> + repo = fixtures.remotes + remotes = null + before (done) -> + repo.remote_list (err, _remotes) -> + remotes = _remotes + done err + + it "is a list of remotes", -> + remotes.should.have.lengthOf 1 + remotes[0].should.eql "origin" + + describe "when there are no remotes", -> + repo = fixtures.branched + it "is an empty Array", -> + repo.remote_list (err, remotes) -> + remotes.should.eql [] + + + describe "#tags", -> + describe "a repo with tags", -> + repo = fixtures.tagged + tags = null + before (done) -> + repo.tags (err, _tags) -> + tags = _tags + done err + + it "is an Array of Tags", -> + tags.should.be.an.instanceof Array + tags[0].should.be.an.instanceof Tag + + it "is the correct tag", -> + tags[0].name.should.eql "tag-1" + + describe "a repo without tags", -> + repo = fixtures.branched + it "is an empty array", (done) -> + repo.tags (err, tags) -> + tags.should.eql [] + done err + + + describe "#delete_tag", -> + describe "deleting a tag that does not exist", -> + repo = fixtures.branched + it "passes an error", (done) -> + repo.delete_tag "nonexistant-tag", (err) -> + should.exist err + done() + + + describe "#branches", -> + repo = fixtures.branched + branches = null + before (done) -> + repo.branches (err, _branches) -> + branches = _branches + done err + + it "is an Array of Heads", -> + branches.should.be.an.instanceof Array + branches[0].should.be.an.instanceof Head + + it "has the correct branches", -> + branches[0].name.should.eql "master" + branches[1].name.should.eql "something" + + + describe "#branch", -> + describe "when a branch name is given", -> + repo = fixtures.branched + branch = null + before (done) -> + repo.branch "something", (err, b) -> + branch = b + done err + + it "is a Head", -> + branch.should.be.an.instanceof Head + + it "has the correct name", -> + branch.name.should.eql "something" + + describe "when no branch name is given", -> + repo = fixtures.branched + branch = null + before (done) -> + repo.branch (err, b) -> + branch = b + done err + + it "has the correct name", -> + branch.name.should.eql "master" + + describe "an invalid branch", -> + repo = fixtures.branched + it "passes an error", (done) -> + repo.branch "nonexistant-branch", (err, b) -> + should.exist err + should.not.exist b + done() + + + describe "#delete_branch", -> + describe "a branch that does not exist", -> + repo = fixtures.branched + it "passes an error", (done) -> + repo.delete_branch "nonexistant-branch", (err) -> + should.exist err + done() + diff --git a/test/status.test.coffee b/test/status.test.coffee new file mode 100644 index 0000000..d568e84 --- /dev/null +++ b/test/status.test.coffee @@ -0,0 +1,53 @@ +should = require 'should' +fixtures = require './fixtures' +git = require '../src' +Status = require '../src/status' + +GIT_STATUS = """ + # On branch master + # Changes to be committed: + # (use "git reset HEAD ..." to unstage) + # + # deleted: crackers.txt + # modified: file.txt + # + # Changed but not updated: + # (use "git add ..." to update what will be committed) + # (use "git checkout -- ..." to discard changes in working directory) + # + # modified: cheese.txt + # + # Untracked files: + # (use "git add ..." to include in what will be committed) + # + # pickles.txt + """ + +describe "Status", -> + describe "()", -> + describe "when there are changes", -> + repo = fixtures.status + status = new Status.Status repo + status.parse GIT_STATUS + + it "has a modified staged file", -> + status.files["file.txt"].staged.should.be.true + status.files["file.txt"].type.should.eql "M" + status.files["file.txt"].tracked.should.be.true + + it "has a modified unstaged file", -> + status.files["cheese.txt"].staged.should.be.false + status.files["cheese.txt"].type.should.eql "M" + status.files["cheese.txt"].tracked.should.be.true + + it "has a deleted file", -> + status.files["crackers.txt"].staged.should.be.true + status.files["crackers.txt"].type.should.eql "D" + status.files["crackers.txt"].tracked.should.be.true + + it "has an untracked file", -> + status.files["pickles.txt"].tracked.should.be.false + should.not.exist status.files["pickles.txt"].type + + it "is not clean", -> + status.clean.should.be.false diff --git a/test/tag.test.coffee b/test/tag.test.coffee new file mode 100644 index 0000000..98627be --- /dev/null +++ b/test/tag.test.coffee @@ -0,0 +1,44 @@ +should = require 'should' +fixtures = require './fixtures' +git = require '../src' +Tag = require '../src/tag' + +describe "Tag", -> + describe ".find_all", -> + repo = fixtures.tagged + tags = null + before (done) -> + Tag.find_all repo, (err, _tags) -> + tags = _tags + done err + + it "is an Array of Tags", -> + tags.should.be.an.instanceof Array + tags[0].should.be.an.instanceof Tag + + pref = "the tag" + it "#{pref} has the correct name", -> + tags[0].name.should.eql "tag-1" + + it "#{pref} has the correct commit", -> + tags[0].commit.id.should.eql "32bbb351de16c3e404b3b7c77601c3d124e1e1a1" + + + describe "#message", -> + repo = fixtures.tagged + tags = null + message = null + before (done) -> + repo.tags (err, _tags) -> + tags = _tags + done err if err + tags[0].message (err, _message) -> + message = _message + done err + + it "is the correct message", -> + message.should.include "the first tag" + + it "has the correct commit", -> + tags[0].commit.message.should.eql "commit 5" + diff --git a/test/tree.test.coffee b/test/tree.test.coffee new file mode 100644 index 0000000..abb93dd --- /dev/null +++ b/test/tree.test.coffee @@ -0,0 +1,119 @@ +should = require 'should' +fixtures = require './fixtures' +git = require '../src' +Tree = require '../src/tree' +Blob = require '../src/blob' +Submodule = require '../src/submodule' + +describe "Tree", -> + describe "#contents", -> + describe "simple", -> + repo = fixtures.branched + tree = repo.tree() + contents = null + + before (done) -> + tree.contents (err, _contents) -> + contents = _contents + done err + + it "is an Array", -> + contents.should.be.an.instanceof Array + + it "contains a Blob", -> + contents[0].should.be.an.instanceof Blob + contents[0].name.should.eql "README.md" + + it "contains a Tree", -> + contents[1].should.be.an.instanceof Tree + contents[1].name.should.eql "some" + + describe "with submodules", -> + repo = fixtures.submodule + tree = repo.tree() + contents = null + + before (done) -> + tree.contents (err, _contents) -> + contents = _contents + done err + + it "contains a Submodule", (done) -> + contents[2].should.be.an.instanceof Submodule + contents[2].name.should.eql "spoon-knife" + contents[2].url (err, url) -> + url.should.eql "git://github.com/octocat/Spoon-Knife.git" + done err + + + describe "#blobs", -> + repo = fixtures.branched + tree = repo.tree() + blobs = null + + before (done) -> + tree.blobs (err, _blobs) -> + blobs = _blobs + done err + + it "has only 1 item", -> + blobs.should.have.lengthOf 1 + + it "contains a Blob", -> + blobs[0].should.be.an.instanceof Blob + blobs[0].name.should.eql "README.md" + + + describe "#trees", -> + repo = fixtures.branched + tree = repo.tree() + trees = null + + before (done) -> + tree.trees (err, _trees) -> + trees = _trees + done err + + it "has only 1 item", -> + trees.should.have.lengthOf 1 + + it "contains a Tree", -> + trees[0].should.be.an.instanceof Tree + trees[0].name.should.eql "some" + + + describe "#find", -> + repo = fixtures.branched + tree = repo.tree() + describe "find a file", -> + blob = null + before (done) -> + tree.find "README.md", (err, _blob) -> + blob = _blob + done err + + it "finds the Blob", -> + blob.should.be.an.instanceof Blob + blob.name.should.eql "README.md" + + describe "find a directory", -> + subtree = null + before (done) -> + tree.find "some", (err, _tree) -> + subtree = _tree + done err + + it "finds the Tree", -> + subtree.should.be.an.instanceof Tree + subtree.name.should.eql "some" + + describe "find a nonexistant file", -> + subtree = null + before (done) -> + tree.find "nonexistant", (err, _tree) -> + subtree = _tree + done err + + it "is null", -> + should.not.exist subtree +