├── .npmignore ├── test ├── fixtures │ ├── status │ │ ├── COMMIT_EDITMSG │ │ ├── HEAD │ │ ├── refs │ │ │ └── heads │ │ │ │ └── master │ │ ├── index │ │ ├── description │ │ ├── config │ │ ├── objects │ │ │ ├── 10 │ │ │ │ └── c14fdc111b6776b1fc3740e8b16939233f7a84 │ │ │ ├── 19 │ │ │ │ └── 0a18037c64c43e6b11489df4bf0b9eb6d2c9bf │ │ │ ├── 8b │ │ │ │ └── aef1b4abc478178b004d62031cf7fe6db6f903 │ │ │ ├── ac │ │ │ │ └── be86c7c89586e0912a0a851bacf309c595c308 │ │ │ ├── d8 │ │ │ │ └── b85abf216872cda37b67d8f0c2d1a6fa81a7c3 │ │ │ └── e6 │ │ │ │ └── 2415c2695333e16ce6a65eac0b0ade587fd391 │ │ ├── logs │ │ │ ├── HEAD │ │ │ └── refs │ │ │ │ └── heads │ │ │ │ └── master │ │ ├── hooks │ │ │ ├── post-commit.sample │ │ │ ├── post-update.sample │ │ │ ├── pre-applypatch.sample │ │ │ ├── applypatch-msg.sample │ │ │ ├── post-receive.sample │ │ │ ├── commit-msg.sample │ │ │ ├── prepare-commit-msg.sample │ │ │ ├── pre-commit.sample │ │ │ ├── update.sample │ │ │ └── pre-rebase.sample │ │ └── info │ │ │ └── exclude │ ├── tagged │ │ ├── COMMIT_EDITMSG │ │ ├── HEAD │ │ ├── refs │ │ │ ├── heads │ │ │ │ └── master │ │ │ └── tags │ │ │ │ └── tag-1 │ │ ├── index │ │ ├── description │ │ ├── config │ │ ├── objects │ │ │ ├── 12 │ │ │ │ └── d411f1066eccc420e1f0361ecf88f8e2b65f5b │ │ │ ├── 19 │ │ │ │ └── 0a18037c64c43e6b11489df4bf0b9eb6d2c9bf │ │ │ ├── 32 │ │ │ │ └── bbb351de16c3e404b3b7c77601c3d124e1e1a1 │ │ │ ├── 41 │ │ │ │ └── 0a16650aa2a129ed639f66b7ce17bd76b90e65 │ │ │ ├── 48 │ │ │ │ └── 082f72f087ce7e6fa75b9c41d7387daecd447b │ │ │ ├── 68 │ │ │ │ └── ba9551aa06079093a6b868b7fac98ed6634ec0 │ │ │ ├── 78 │ │ │ │ └── cb34985ee281f9b8008b556ad5b4dd36473b10 │ │ │ ├── 81 │ │ │ │ └── c545efebe5f57d4cab2ba9ec294c4b0cadf672 │ │ │ ├── 91 │ │ │ │ └── d6d39cd8638f1067989e0ab9c9c00ca378c952 │ │ │ ├── 2c │ │ │ │ └── 2ecd34cc1d33002db81285b50396ab597d10bd │ │ │ ├── 9f │ │ │ │ └── 358a4addefcab294b83e4282bfef1f9625a249 │ │ │ ├── bb │ │ │ │ └── fb0e8e077ab9b37941097883f4e917ecca3d33 │ │ │ ├── d0 │ │ │ │ └── 0491fd7e5bb6fa28c517a0bb32b8b506539d4d │ │ │ ├── d6 │ │ │ │ └── 68469ca17a197feaa175a83f2f3de2abb781ad │ │ │ ├── e5 │ │ │ │ └── 6e15bb7ddb6bd0b6d924b18fcee53d8713d7ea │ │ │ ├── e6 │ │ │ │ └── 380addaa18a80d1ce0ed1ebdf3417a654f6fde │ │ │ ├── ea │ │ │ │ └── 1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c │ │ │ ├── eb │ │ │ │ └── cbdf47e0ad6503d09f59cd8eeb76d9435a0396 │ │ │ └── f8 │ │ │ │ └── 44a55bbc98af479703c35aeb2837cf0e4eff1a │ │ ├── hooks │ │ │ ├── post-commit.sample │ │ │ ├── post-update.sample │ │ │ ├── pre-applypatch.sample │ │ │ ├── applypatch-msg.sample │ │ │ ├── post-receive.sample │ │ │ ├── commit-msg.sample │ │ │ ├── prepare-commit-msg.sample │ │ │ ├── pre-commit.sample │ │ │ ├── update.sample │ │ │ └── pre-rebase.sample │ │ ├── info │ │ │ └── exclude │ │ └── logs │ │ │ ├── HEAD │ │ │ └── refs │ │ │ └── heads │ │ │ └── master │ ├── checkout │ │ ├── COMMIT_EDITMSG │ │ ├── HEAD │ │ ├── refs │ │ │ └── heads │ │ │ │ ├── b2 │ │ │ │ ├── b3 │ │ │ │ └── master │ │ ├── index │ │ ├── description │ │ ├── config │ │ ├── objects │ │ │ ├── 5d │ │ │ │ └── 0a23d3d71875e5abe637a1b7b3b9ea050f16aa │ │ │ ├── e5 │ │ │ │ └── fa16f39fe47d5562b74c4f5912d8ef07dd126a │ │ │ └── e6 │ │ │ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 │ │ ├── logs │ │ │ └── refs │ │ │ │ └── heads │ │ │ │ ├── b2 │ │ │ │ ├── b3 │ │ │ │ └── master │ │ ├── hooks │ │ │ ├── post-commit.sample │ │ │ ├── post-update.sample │ │ │ ├── pre-applypatch.sample │ │ │ ├── applypatch-msg.sample │ │ │ ├── post-receive.sample │ │ │ ├── commit-msg.sample │ │ │ ├── prepare-commit-msg.sample │ │ │ ├── pre-commit.sample │ │ │ ├── update.sample │ │ │ └── pre-rebase.sample │ │ └── info │ │ │ └── exclude │ ├── remotes │ │ ├── HEAD │ │ ├── refs │ │ │ ├── remotes │ │ │ │ └── origin │ │ │ │ │ └── HEAD │ │ │ └── heads │ │ │ │ └── master │ │ ├── index │ │ ├── description │ │ ├── packed-refs │ │ ├── objects │ │ │ └── pack │ │ │ │ ├── pack-3f24024466408b565d66ecd5902c84ec2362e513.idx │ │ │ │ └── pack-3f24024466408b565d66ecd5902c84ec2362e513.pack │ │ ├── logs │ │ │ ├── HEAD │ │ │ └── refs │ │ │ │ └── heads │ │ │ │ └── master │ │ ├── hooks │ │ │ ├── post-commit.sample │ │ │ ├── post-update.sample │ │ │ ├── pre-applypatch.sample │ │ │ ├── applypatch-msg.sample │ │ │ ├── post-receive.sample │ │ │ ├── commit-msg.sample │ │ │ ├── prepare-commit-msg.sample │ │ │ ├── pre-commit.sample │ │ │ ├── update.sample │ │ │ └── pre-rebase.sample │ │ ├── info │ │ │ └── exclude │ │ └── config │ ├── simple │ │ ├── HEAD │ │ ├── index │ │ ├── description │ │ ├── config │ │ ├── hooks │ │ │ ├── post-commit.sample │ │ │ ├── post-update.sample │ │ │ ├── pre-applypatch.sample │ │ │ ├── applypatch-msg.sample │ │ │ ├── post-receive.sample │ │ │ ├── commit-msg.sample │ │ │ ├── prepare-commit-msg.sample │ │ │ ├── pre-commit.sample │ │ │ ├── update.sample │ │ │ └── pre-rebase.sample │ │ └── info │ │ │ └── exclude │ ├── branched │ │ ├── COMMIT_EDITMSG │ │ ├── HEAD │ │ ├── refs │ │ │ └── heads │ │ │ │ ├── master │ │ │ │ └── something │ │ ├── index │ │ ├── description │ │ ├── config │ │ ├── objects │ │ │ ├── 17 │ │ │ │ └── 2b99dd464f6c9c101a987991fcc63491f08985 │ │ │ ├── 77 │ │ │ │ └── 83d64e077617ab7b84fe9726f721e921b9623b │ │ │ ├── 91 │ │ │ │ └── 3318e66e9beed3e89e9c402c1d6585ef3f7e6f │ │ │ ├── 6f │ │ │ │ └── 1de2421ce0427d9243bcd4ba7e38734cf96fe4 │ │ │ ├── 9a │ │ │ │ └── bc538b0dc8bf3974e04f3cb5d3f06240e8cef6 │ │ │ ├── b0 │ │ │ │ └── 45ec4b14c60f03329654266478b6e8b92b33c5 │ │ │ ├── b6 │ │ │ │ └── 1b4728ef3cb2b3de0ec0421a54d170b6b7de13 │ │ │ ├── bd │ │ │ │ └── 502d58e9f0d51bd6c880994f778d0c896e4135 │ │ │ ├── c0 │ │ │ │ └── efd1cc406c54014067a7da9ab15c6a9c1bc714 │ │ │ └── e4 │ │ │ │ └── ff69dd8f19d770e9731b4bc424ccb695f0b5ad │ │ ├── hooks │ │ │ ├── post-commit.sample │ │ │ ├── post-update.sample │ │ │ ├── pre-applypatch.sample │ │ │ ├── applypatch-msg.sample │ │ │ ├── post-receive.sample │ │ │ ├── commit-msg.sample │ │ │ ├── prepare-commit-msg.sample │ │ │ ├── pre-commit.sample │ │ │ ├── update.sample │ │ │ └── pre-rebase.sample │ │ ├── info │ │ │ └── exclude │ │ └── logs │ │ │ ├── refs │ │ │ └── heads │ │ │ │ ├── master │ │ │ │ └── something │ │ │ └── HEAD │ └── submodule │ │ ├── COMMIT_EDITMSG │ │ ├── HEAD │ │ ├── refs │ │ └── heads │ │ │ └── master │ │ ├── index │ │ ├── description │ │ ├── config │ │ ├── objects │ │ ├── 45 │ │ │ └── b983be36b73c0788dc9cbcb76cbb80fc7bb057 │ │ ├── 48 │ │ │ └── 7a70ae6cae1041f9903701d0cd8c9f30dd88a9 │ │ ├── 3b │ │ │ └── 563fc33e799367e2d2824fd21e0219fc7591db │ │ ├── b0 │ │ │ └── e66a8a93b83161375f18dcdc9e9329af61e04f │ │ ├── ea │ │ │ └── 4ab982469a4a539b773a091670224343db1eee │ │ └── fb │ │ │ └── eb68ceaa10264b36573022d784137bab4fa273 │ │ ├── hooks │ │ ├── post-commit.sample │ │ ├── post-update.sample │ │ ├── pre-applypatch.sample │ │ ├── applypatch-msg.sample │ │ ├── post-receive.sample │ │ ├── commit-msg.sample │ │ ├── prepare-commit-msg.sample │ │ ├── pre-commit.sample │ │ ├── update.sample │ │ └── pre-rebase.sample │ │ ├── info │ │ └── exclude │ │ └── logs │ │ ├── HEAD │ │ └── refs │ │ └── heads │ │ └── master ├── mocha.opts ├── fixtures.coffee ├── commit.test.coffee ├── config.test.coffee ├── tag.test.coffee ├── actor.test.coffee ├── index.test.coffee ├── ref.test.coffee ├── status.test.coffee ├── blob.test.coffee ├── tree.test.coffee └── diff.test.coffee ├── .gitattributes ├── .travis.yml ├── .gitignore ├── src ├── config.coffee ├── actor.coffee ├── status.coffee ├── blob.coffee ├── index.coffee ├── ref.coffee ├── tag.coffee ├── submodule.coffee ├── git.coffee ├── tree.coffee ├── diff.coffee └── commit.coffee ├── package.json ├── LICENSE └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | test 3 | -------------------------------------------------------------------------------- /test/fixtures/status/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | initial 2 | -------------------------------------------------------------------------------- /test/fixtures/tagged/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | commit 6 2 | -------------------------------------------------------------------------------- /test/fixtures/checkout/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | commit 1 2 | -------------------------------------------------------------------------------- /test/fixtures/remotes/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/fixtures/simple/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/fixtures/status/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/fixtures/tagged/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/fixtures/branched/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | add a sub dir 2 | -------------------------------------------------------------------------------- /test/fixtures/branched/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/fixtures/checkout/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/fixtures/submodule/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | add a submodule 2 | -------------------------------------------------------------------------------- /test/fixtures/submodule/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | -R spec 2 | --compilers coffee:coffee-script -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # To avoid corrupting fixtures 2 | test/fixtures/ binary 3 | -------------------------------------------------------------------------------- /test/fixtures/checkout/refs/heads/b2: -------------------------------------------------------------------------------- 1 | 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 2 | -------------------------------------------------------------------------------- /test/fixtures/checkout/refs/heads/b3: -------------------------------------------------------------------------------- 1 | 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 2 | -------------------------------------------------------------------------------- /test/fixtures/remotes/refs/remotes/origin/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/remotes/origin/master 2 | -------------------------------------------------------------------------------- /test/fixtures/status/refs/heads/master: -------------------------------------------------------------------------------- 1 | 10c14fdc111b6776b1fc3740e8b16939233f7a84 2 | -------------------------------------------------------------------------------- /test/fixtures/tagged/refs/heads/master: -------------------------------------------------------------------------------- 1 | 78cb34985ee281f9b8008b556ad5b4dd36473b10 2 | -------------------------------------------------------------------------------- /test/fixtures/tagged/refs/tags/tag-1: -------------------------------------------------------------------------------- 1 | e6380addaa18a80d1ce0ed1ebdf3417a654f6fde 2 | -------------------------------------------------------------------------------- /test/fixtures/branched/refs/heads/master: -------------------------------------------------------------------------------- 1 | 913318e66e9beed3e89e9c402c1d6585ef3f7e6f 2 | -------------------------------------------------------------------------------- /test/fixtures/branched/refs/heads/something: -------------------------------------------------------------------------------- 1 | 7783d64e077617ab7b84fe9726f721e921b9623b 2 | -------------------------------------------------------------------------------- /test/fixtures/checkout/refs/heads/master: -------------------------------------------------------------------------------- 1 | 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa 2 | -------------------------------------------------------------------------------- /test/fixtures/remotes/refs/heads/master: -------------------------------------------------------------------------------- 1 | bdd3996d38d885e18e5c5960df1c2c06e34d673f 2 | -------------------------------------------------------------------------------- /test/fixtures/submodule/refs/heads/master: -------------------------------------------------------------------------------- 1 | fbeb68ceaa10264b36573022d784137bab4fa273 2 | -------------------------------------------------------------------------------- /test/fixtures/branched/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/index -------------------------------------------------------------------------------- /test/fixtures/checkout/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/checkout/index -------------------------------------------------------------------------------- /test/fixtures/remotes/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/remotes/index -------------------------------------------------------------------------------- /test/fixtures/simple/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/simple/index -------------------------------------------------------------------------------- /test/fixtures/status/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/status/index -------------------------------------------------------------------------------- /test/fixtures/tagged/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/index -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | # - "0.6" 4 | - "0.8" 5 | - "0.10" 6 | - "0.11" 7 | -------------------------------------------------------------------------------- /test/fixtures/submodule/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/submodule/index -------------------------------------------------------------------------------- /test/fixtures/branched/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /test/fixtures/checkout/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /test/fixtures/remotes/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /test/fixtures/simple/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /test/fixtures/status/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /test/fixtures/submodule/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /test/fixtures/tagged/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /test/fixtures/remotes/packed-refs: -------------------------------------------------------------------------------- 1 | # pack-refs with: peeled 2 | bdd3996d38d885e18e5c5960df1c2c06e34d673f refs/remotes/origin/master 3 | -------------------------------------------------------------------------------- /test/fixtures/branched/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | -------------------------------------------------------------------------------- /test/fixtures/checkout/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | -------------------------------------------------------------------------------- /test/fixtures/simple/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | -------------------------------------------------------------------------------- /test/fixtures/status/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | -------------------------------------------------------------------------------- /test/fixtures/submodule/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | -------------------------------------------------------------------------------- /test/fixtures/tagged/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore emacs and other editor files 2 | *~ 3 | \#*\# 4 | .\#* 5 | *.swp 6 | *.swo 7 | 8 | src/*.js 9 | test/*.js 10 | 11 | lib/ 12 | node_modules/ 13 | 14 | -------------------------------------------------------------------------------- /test/fixtures/status/objects/10/c14fdc111b6776b1fc3740e8b16939233f7a84: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/status/objects/10/c14fdc111b6776b1fc3740e8b16939233f7a84 -------------------------------------------------------------------------------- /test/fixtures/status/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/status/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf -------------------------------------------------------------------------------- /test/fixtures/status/objects/8b/aef1b4abc478178b004d62031cf7fe6db6f903: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/status/objects/8b/aef1b4abc478178b004d62031cf7fe6db6f903 -------------------------------------------------------------------------------- /test/fixtures/status/objects/ac/be86c7c89586e0912a0a851bacf309c595c308: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/status/objects/ac/be86c7c89586e0912a0a851bacf309c595c308 -------------------------------------------------------------------------------- /test/fixtures/status/objects/d8/b85abf216872cda37b67d8f0c2d1a6fa81a7c3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/status/objects/d8/b85abf216872cda37b67d8f0c2d1a6fa81a7c3 -------------------------------------------------------------------------------- /test/fixtures/status/objects/e6/2415c2695333e16ce6a65eac0b0ade587fd391: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/status/objects/e6/2415c2695333e16ce6a65eac0b0ade587fd391 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/12/d411f1066eccc420e1f0361ecf88f8e2b65f5b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/12/d411f1066eccc420e1f0361ecf88f8e2b65f5b -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/2c/2ecd34cc1d33002db81285b50396ab597d10bd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/2c/2ecd34cc1d33002db81285b50396ab597d10bd -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/32/bbb351de16c3e404b3b7c77601c3d124e1e1a1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/32/bbb351de16c3e404b3b7c77601c3d124e1e1a1 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/41/0a16650aa2a129ed639f66b7ce17bd76b90e65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/41/0a16650aa2a129ed639f66b7ce17bd76b90e65 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/48/082f72f087ce7e6fa75b9c41d7387daecd447b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/48/082f72f087ce7e6fa75b9c41d7387daecd447b -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/68/ba9551aa06079093a6b868b7fac98ed6634ec0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/68/ba9551aa06079093a6b868b7fac98ed6634ec0 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/78/cb34985ee281f9b8008b556ad5b4dd36473b10: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/78/cb34985ee281f9b8008b556ad5b4dd36473b10 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/81/c545efebe5f57d4cab2ba9ec294c4b0cadf672: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/81/c545efebe5f57d4cab2ba9ec294c4b0cadf672 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/91/d6d39cd8638f1067989e0ab9c9c00ca378c952: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/91/d6d39cd8638f1067989e0ab9c9c00ca378c952 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/9f/358a4addefcab294b83e4282bfef1f9625a249: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/9f/358a4addefcab294b83e4282bfef1f9625a249 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/bb/fb0e8e077ab9b37941097883f4e917ecca3d33: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/bb/fb0e8e077ab9b37941097883f4e917ecca3d33 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/d0/0491fd7e5bb6fa28c517a0bb32b8b506539d4d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/d0/0491fd7e5bb6fa28c517a0bb32b8b506539d4d -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/d6/68469ca17a197feaa175a83f2f3de2abb781ad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/d6/68469ca17a197feaa175a83f2f3de2abb781ad -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/e5/6e15bb7ddb6bd0b6d924b18fcee53d8713d7ea: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/e5/6e15bb7ddb6bd0b6d924b18fcee53d8713d7ea -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/e6/380addaa18a80d1ce0ed1ebdf3417a654f6fde: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/e6/380addaa18a80d1ce0ed1ebdf3417a654f6fde -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/ea/1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/ea/1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/eb/cbdf47e0ad6503d09f59cd8eeb76d9435a0396: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/eb/cbdf47e0ad6503d09f59cd8eeb76d9435a0396 -------------------------------------------------------------------------------- /test/fixtures/tagged/objects/f8/44a55bbc98af479703c35aeb2837cf0e4eff1a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/tagged/objects/f8/44a55bbc98af479703c35aeb2837cf0e4eff1a -------------------------------------------------------------------------------- /test/fixtures/branched/objects/17/2b99dd464f6c9c101a987991fcc63491f08985: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/17/2b99dd464f6c9c101a987991fcc63491f08985 -------------------------------------------------------------------------------- /test/fixtures/branched/objects/6f/1de2421ce0427d9243bcd4ba7e38734cf96fe4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/6f/1de2421ce0427d9243bcd4ba7e38734cf96fe4 -------------------------------------------------------------------------------- /test/fixtures/branched/objects/77/83d64e077617ab7b84fe9726f721e921b9623b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/77/83d64e077617ab7b84fe9726f721e921b9623b -------------------------------------------------------------------------------- /test/fixtures/branched/objects/91/3318e66e9beed3e89e9c402c1d6585ef3f7e6f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/91/3318e66e9beed3e89e9c402c1d6585ef3f7e6f -------------------------------------------------------------------------------- /test/fixtures/branched/objects/9a/bc538b0dc8bf3974e04f3cb5d3f06240e8cef6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/9a/bc538b0dc8bf3974e04f3cb5d3f06240e8cef6 -------------------------------------------------------------------------------- /test/fixtures/branched/objects/b0/45ec4b14c60f03329654266478b6e8b92b33c5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/b0/45ec4b14c60f03329654266478b6e8b92b33c5 -------------------------------------------------------------------------------- /test/fixtures/branched/objects/b6/1b4728ef3cb2b3de0ec0421a54d170b6b7de13: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/b6/1b4728ef3cb2b3de0ec0421a54d170b6b7de13 -------------------------------------------------------------------------------- /test/fixtures/branched/objects/bd/502d58e9f0d51bd6c880994f778d0c896e4135: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/bd/502d58e9f0d51bd6c880994f778d0c896e4135 -------------------------------------------------------------------------------- /test/fixtures/branched/objects/c0/efd1cc406c54014067a7da9ab15c6a9c1bc714: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/c0/efd1cc406c54014067a7da9ab15c6a9c1bc714 -------------------------------------------------------------------------------- /test/fixtures/branched/objects/e4/ff69dd8f19d770e9731b4bc424ccb695f0b5ad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/branched/objects/e4/ff69dd8f19d770e9731b4bc424ccb695f0b5ad -------------------------------------------------------------------------------- /test/fixtures/checkout/objects/5d/0a23d3d71875e5abe637a1b7b3b9ea050f16aa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/checkout/objects/5d/0a23d3d71875e5abe637a1b7b3b9ea050f16aa -------------------------------------------------------------------------------- /test/fixtures/checkout/objects/e5/fa16f39fe47d5562b74c4f5912d8ef07dd126a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/checkout/objects/e5/fa16f39fe47d5562b74c4f5912d8ef07dd126a -------------------------------------------------------------------------------- /test/fixtures/checkout/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/checkout/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 -------------------------------------------------------------------------------- /test/fixtures/submodule/objects/3b/563fc33e799367e2d2824fd21e0219fc7591db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/submodule/objects/3b/563fc33e799367e2d2824fd21e0219fc7591db -------------------------------------------------------------------------------- /test/fixtures/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 -------------------------------------------------------------------------------- /test/fixtures/submodule/objects/48/7a70ae6cae1041f9903701d0cd8c9f30dd88a9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/submodule/objects/48/7a70ae6cae1041f9903701d0cd8c9f30dd88a9 -------------------------------------------------------------------------------- /test/fixtures/submodule/objects/b0/e66a8a93b83161375f18dcdc9e9329af61e04f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/submodule/objects/b0/e66a8a93b83161375f18dcdc9e9329af61e04f -------------------------------------------------------------------------------- /test/fixtures/submodule/objects/ea/4ab982469a4a539b773a091670224343db1eee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/submodule/objects/ea/4ab982469a4a539b773a091670224343db1eee -------------------------------------------------------------------------------- /test/fixtures/submodule/objects/fb/eb68ceaa10264b36573022d784137bab4fa273: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/submodule/objects/fb/eb68ceaa10264b36573022d784137bab4fa273 -------------------------------------------------------------------------------- /test/fixtures/status/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 10c14fdc111b6776b1fc3740e8b16939233f7a84 sentientwaffle 1329174405 -0700 commit (initial): initial 2 | -------------------------------------------------------------------------------- /test/fixtures/checkout/logs/refs/heads/b2: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248175 -0700 branch: Created from HEAD 2 | -------------------------------------------------------------------------------- /test/fixtures/checkout/logs/refs/heads/b3: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248189 -0700 branch: Created from HEAD 2 | -------------------------------------------------------------------------------- /test/fixtures/checkout/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 5d0a23d3d71875e5abe637a1b7b3b9ea050f16aa sentientwaffle 1329248161 -0700 commit (initial): commit 1 2 | -------------------------------------------------------------------------------- /test/fixtures/remotes/objects/pack/pack-3f24024466408b565d66ecd5902c84ec2362e513.idx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/remotes/objects/pack/pack-3f24024466408b565d66ecd5902c84ec2362e513.idx -------------------------------------------------------------------------------- /test/fixtures/remotes/objects/pack/pack-3f24024466408b565d66ecd5902c84ec2362e513.pack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pose/gift/master/test/fixtures/remotes/objects/pack/pack-3f24024466408b565d66ecd5902c84ec2362e513.pack -------------------------------------------------------------------------------- /test/fixtures/status/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 10c14fdc111b6776b1fc3740e8b16939233f7a84 sentientwaffle 1329174405 -0700 commit (initial): initial 2 | -------------------------------------------------------------------------------- /test/fixtures/remotes/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 bdd3996d38d885e18e5c5960df1c2c06e34d673f sentientwaffle 1329101708 -0700 clone: from git@github.com:octocat/Spoon-Knife.git 2 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/post-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script that is called after a successful 4 | # commit is made. 5 | # 6 | # To enable this hook, rename this file to "post-commit". 7 | 8 | : Nothing 9 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/post-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script that is called after a successful 4 | # commit is made. 5 | # 6 | # To enable this hook, rename this file to "post-commit". 7 | 8 | : Nothing 9 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/post-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script that is called after a successful 4 | # commit is made. 5 | # 6 | # To enable this hook, rename this file to "post-commit". 7 | 8 | : Nothing 9 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/post-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script that is called after a successful 4 | # commit is made. 5 | # 6 | # To enable this hook, rename this file to "post-commit". 7 | 8 | : Nothing 9 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/post-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script that is called after a successful 4 | # commit is made. 5 | # 6 | # To enable this hook, rename this file to "post-commit". 7 | 8 | : Nothing 9 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/post-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script that is called after a successful 4 | # commit is made. 5 | # 6 | # To enable this hook, rename this file to "post-commit". 7 | 8 | : Nothing 9 | -------------------------------------------------------------------------------- /test/fixtures/remotes/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 bdd3996d38d885e18e5c5960df1c2c06e34d673f sentientwaffle 1329101708 -0700 clone: from git@github.com:octocat/Spoon-Knife.git 2 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/post-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script that is called after a successful 4 | # commit is made. 5 | # 6 | # To enable this hook, rename this file to "post-commit". 7 | 8 | : Nothing 9 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git-update-server-info 9 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git-update-server-info 9 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git-update-server-info 9 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git-update-server-info 9 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git-update-server-info 9 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git-update-server-info 9 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git-update-server-info 9 | -------------------------------------------------------------------------------- /test/fixtures/remotes/info/exclude: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /test/fixtures/simple/info/exclude: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /test/fixtures/status/info/exclude: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /test/fixtures/tagged/info/exclude: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /test/fixtures/branched/info/exclude: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /test/fixtures/checkout/info/exclude: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /test/fixtures/submodule/info/exclude: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /test/fixtures/remotes/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | [remote "origin"] 7 | fetch = +refs/heads/*:refs/remotes/origin/* 8 | url = git@github.com:octocat/Spoon-Knife.git 9 | [branch "master"] 10 | remote = origin 11 | merge = refs/heads/master 12 | -------------------------------------------------------------------------------- /test/fixtures/submodule/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 ea4ab982469a4a539b773a091670224343db1eee sentientwaffle 1329251828 -0700 commit (initial): initial 2 | ea4ab982469a4a539b773a091670224343db1eee fbeb68ceaa10264b36573022d784137bab4fa273 sentientwaffle 1329252042 -0700 commit: add a submodule 3 | -------------------------------------------------------------------------------- /test/fixtures/branched/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999490 -0700 commit (initial): 1 2 | b045ec4b14c60f03329654266478b6e8b92b33c5 913318e66e9beed3e89e9c402c1d6585ef3f7e6f sentientwaffle 1329013671 -0700 commit: add a sub dir 3 | -------------------------------------------------------------------------------- /test/fixtures/branched/logs/refs/heads/something: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999503 -0700 branch: Created from HEAD 2 | b045ec4b14c60f03329654266478b6e8b92b33c5 7783d64e077617ab7b84fe9726f721e921b9623b sentientwaffle 1328999518 -0700 commit: 2 3 | -------------------------------------------------------------------------------- /test/fixtures/submodule/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 ea4ab982469a4a539b773a091670224343db1eee sentientwaffle 1329251828 -0700 commit (initial): initial 2 | ea4ab982469a4a539b773a091670224343db1eee fbeb68ceaa10264b36573022d784137bab4fa273 sentientwaffle 1329252042 -0700 commit: add a submodule 3 | -------------------------------------------------------------------------------- /test/fixtures.coffee: -------------------------------------------------------------------------------- 1 | git = require '../src' 2 | 3 | dir = "#{__dirname}/fixtures" 4 | 5 | module.exports = 6 | branched: git("#{dir}/branched", true) 7 | checkout: git("#{dir}/checkout", true) 8 | remotes: git("#{dir}/remotes", true) 9 | simple: git("#{dir}/simple", true) 10 | status: git("#{dir}/status", true) 11 | submodule: git("#{dir}/submodule", true) 12 | tagged: git("#{dir}/tagged", true) 13 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | test -x "$GIT_DIR/hooks/pre-commit" && 13 | exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | test -x "$GIT_DIR/hooks/pre-commit" && 13 | exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | test -x "$GIT_DIR/hooks/pre-commit" && 13 | exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | test -x "$GIT_DIR/hooks/pre-commit" && 13 | exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | test -x "$GIT_DIR/hooks/pre-commit" && 13 | exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | test -x "$GIT_DIR/hooks/pre-commit" && 13 | exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | test -x "$GIT_DIR/hooks/pre-commit" && 13 | exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /src/config.coffee: -------------------------------------------------------------------------------- 1 | module.exports = C = (repo, callback) -> 2 | repo.git "config", {list: true}, (err, stdout, stderr) -> 3 | config = new Config repo 4 | config.parse stdout 5 | callback err, config 6 | 7 | C.Config = class Config 8 | constructor: (@repo) -> 9 | 10 | # Internal: Parse the config from stdout of a `git config` command 11 | parse: (text)-> 12 | @items = {} 13 | for line in text.split("\n") 14 | if line.length == 0 15 | continue 16 | [key, value] = line.split('=') 17 | @items[key] = value 18 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | test -x "$GIT_DIR/hooks/commit-msg" && 14 | exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | test -x "$GIT_DIR/hooks/commit-msg" && 14 | exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | test -x "$GIT_DIR/hooks/commit-msg" && 14 | exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | test -x "$GIT_DIR/hooks/commit-msg" && 14 | exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | test -x "$GIT_DIR/hooks/commit-msg" && 14 | exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | test -x "$GIT_DIR/hooks/commit-msg" && 14 | exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | test -x "$GIT_DIR/hooks/commit-msg" && 14 | exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /src/actor.coffee: -------------------------------------------------------------------------------- 1 | crypto = require 'crypto' 2 | 3 | module.exports = class Actor 4 | constructor: (@name, @email) -> 5 | if @email 6 | @hash = crypto.createHash("md5").update(@email, "ascii").digest("hex") 7 | 8 | # Public: Get a string representation of the Actor. 9 | toString: -> 10 | "#{@name} <#{@email}>" 11 | 12 | # Public: Parse an Actor from a "bla " string. 13 | # 14 | # Returns Actor. 15 | @from_string: (str) -> 16 | if /<.+>/.test str 17 | [m, name, email] = /(.*) <(.+?)>/.exec str 18 | return new Actor(name, email) 19 | else 20 | return new Actor(str, null) 21 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/post-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script for the "post-receive" event. 4 | # 5 | # The "post-receive" script is run after receive-pack has accepted a pack 6 | # and the repository has been updated. It is passed arguments in through 7 | # stdin in the form 8 | # 9 | # For example: 10 | # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master 11 | # 12 | # see contrib/hooks/ for a sample, or uncomment the next line and 13 | # rename the file to "post-receive". 14 | 15 | #. /usr/share/doc/git-core/contrib/hooks/post-receive-email 16 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/post-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script for the "post-receive" event. 4 | # 5 | # The "post-receive" script is run after receive-pack has accepted a pack 6 | # and the repository has been updated. It is passed arguments in through 7 | # stdin in the form 8 | # 9 | # For example: 10 | # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master 11 | # 12 | # see contrib/hooks/ for a sample, or uncomment the next line and 13 | # rename the file to "post-receive". 14 | 15 | #. /usr/share/doc/git-core/contrib/hooks/post-receive-email 16 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/post-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script for the "post-receive" event. 4 | # 5 | # The "post-receive" script is run after receive-pack has accepted a pack 6 | # and the repository has been updated. It is passed arguments in through 7 | # stdin in the form 8 | # 9 | # For example: 10 | # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master 11 | # 12 | # see contrib/hooks/ for a sample, or uncomment the next line and 13 | # rename the file to "post-receive". 14 | 15 | #. /usr/share/doc/git-core/contrib/hooks/post-receive-email 16 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/post-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script for the "post-receive" event. 4 | # 5 | # The "post-receive" script is run after receive-pack has accepted a pack 6 | # and the repository has been updated. It is passed arguments in through 7 | # stdin in the form 8 | # 9 | # For example: 10 | # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master 11 | # 12 | # see contrib/hooks/ for a sample, or uncomment the next line and 13 | # rename the file to "post-receive". 14 | 15 | #. /usr/share/doc/git-core/contrib/hooks/post-receive-email 16 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/post-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script for the "post-receive" event. 4 | # 5 | # The "post-receive" script is run after receive-pack has accepted a pack 6 | # and the repository has been updated. It is passed arguments in through 7 | # stdin in the form 8 | # 9 | # For example: 10 | # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master 11 | # 12 | # see contrib/hooks/ for a sample, or uncomment the next line and 13 | # rename the file to "post-receive". 14 | 15 | #. /usr/share/doc/git-core/contrib/hooks/post-receive-email 16 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/post-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script for the "post-receive" event. 4 | # 5 | # The "post-receive" script is run after receive-pack has accepted a pack 6 | # and the repository has been updated. It is passed arguments in through 7 | # stdin in the form 8 | # 9 | # For example: 10 | # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master 11 | # 12 | # see contrib/hooks/ for a sample, or uncomment the next line and 13 | # rename the file to "post-receive". 14 | 15 | #. /usr/share/doc/git-core/contrib/hooks/post-receive-email 16 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/post-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script for the "post-receive" event. 4 | # 5 | # The "post-receive" script is run after receive-pack has accepted a pack 6 | # and the repository has been updated. It is passed arguments in through 7 | # stdin in the form 8 | # 9 | # For example: 10 | # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master 11 | # 12 | # see contrib/hooks/ for a sample, or uncomment the next line and 13 | # rename the file to "post-receive". 14 | 15 | #. /usr/share/doc/git-core/contrib/hooks/post-receive-email 16 | -------------------------------------------------------------------------------- /src/status.coffee: -------------------------------------------------------------------------------- 1 | # Public: Create a Status. 2 | # 3 | # repo - A Repo. 4 | # callback - Receives `(err, status)` 5 | # 6 | module.exports = S = (repo, callback) -> 7 | repo.git "status --porcelain", (err, stdout, stderr) -> 8 | status = new Status repo 9 | status.parse stdout 10 | return callback err, status 11 | 12 | S.Status = class Status 13 | constructor: (@repo) -> 14 | 15 | # Internal: Parse the status from stdout of a `git status` command. 16 | parse: (text) -> 17 | @files = {} 18 | @clean = text.length == 0 19 | for line in text.split("\n") 20 | if line.length == 0 21 | continue 22 | file = line.substr 3 23 | type = line.substr 0,2 24 | @files[file] = { staged: (line[0] != " " and line[0] != "?" ) , tracked: line[0] != "?" } 25 | if type != "??" 26 | @files[file].type = type.trim() 27 | -------------------------------------------------------------------------------- /test/fixtures/tagged/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 91d6d39cd8638f1067989e0ab9c9c00ca378c952 sentientwaffle 1329090329 -0700 commit (initial): commit 1 2 | 91d6d39cd8638f1067989e0ab9c9c00ca378c952 ebcbdf47e0ad6503d09f59cd8eeb76d9435a0396 sentientwaffle 1329090345 -0700 commit: commit 2 3 | ebcbdf47e0ad6503d09f59cd8eeb76d9435a0396 ea1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c sentientwaffle 1329090351 -0700 commit: commit 3 4 | ea1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c bbfb0e8e077ab9b37941097883f4e917ecca3d33 sentientwaffle 1329090358 -0700 commit: commit 4 5 | bbfb0e8e077ab9b37941097883f4e917ecca3d33 32bbb351de16c3e404b3b7c77601c3d124e1e1a1 sentientwaffle 1329090365 -0700 commit: commit 5 6 | 32bbb351de16c3e404b3b7c77601c3d124e1e1a1 78cb34985ee281f9b8008b556ad5b4dd36473b10 sentientwaffle 1329090407 -0700 commit: commit 6 7 | -------------------------------------------------------------------------------- /test/fixtures/tagged/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 91d6d39cd8638f1067989e0ab9c9c00ca378c952 sentientwaffle 1329090329 -0700 commit (initial): commit 1 2 | 91d6d39cd8638f1067989e0ab9c9c00ca378c952 ebcbdf47e0ad6503d09f59cd8eeb76d9435a0396 sentientwaffle 1329090345 -0700 commit: commit 2 3 | ebcbdf47e0ad6503d09f59cd8eeb76d9435a0396 ea1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c sentientwaffle 1329090351 -0700 commit: commit 3 4 | ea1658a2ba3f3c4f9f50fa19765ba7d0b5dcd27c bbfb0e8e077ab9b37941097883f4e917ecca3d33 sentientwaffle 1329090358 -0700 commit: commit 4 5 | bbfb0e8e077ab9b37941097883f4e917ecca3d33 32bbb351de16c3e404b3b7c77601c3d124e1e1a1 sentientwaffle 1329090365 -0700 commit: commit 5 6 | 32bbb351de16c3e404b3b7c77601c3d124e1e1a1 78cb34985ee281f9b8008b556ad5b4dd36473b10 sentientwaffle 1329090407 -0700 commit: commit 6 7 | -------------------------------------------------------------------------------- /src/blob.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | 3 | module.exports = class Blob 4 | constructor: (@repo, attrs) -> 5 | {@id, @name, @mode} = attrs 6 | 7 | # Public: Get the blob contents. 8 | # 9 | # callback - Receives `(err, data)`. 10 | # 11 | # Warning, this only returns files less than 200k, the standard buffer size for 12 | # node's exec(). If you need to get bigger files, you should use dataStream() to 13 | # get a stream for the file's data 14 | # 15 | data: (callback) -> 16 | @repo.git "cat-file", {p: true}, @id 17 | , (err, stdout, stderr) -> 18 | return callback err, stdout 19 | 20 | # Public: Get the blob contents as a stream 21 | # 22 | # returns - [dataStream, errstream] 23 | # 24 | # Usage: 25 | # [blobstream, _] = blob.dataStream() 26 | # blobstream.pipe(res) 27 | # 28 | dataStream: () -> 29 | streams = @repo.git.streamCmd "cat-file", {p: true}, [@id] 30 | return streams 31 | 32 | toString: -> 33 | "#" 34 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by git-commit with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by git-commit with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by git-commit with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by git-commit with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by git-commit with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by git-commit with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by git-commit with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gift", 3 | "version": "0.4.1", 4 | "description": "a Git wrapper library", 5 | "keywords": [ 6 | "git", 7 | "cli", 8 | "wrapper" 9 | ], 10 | "homepage": "https://github.com/notatestuser/gift", 11 | "bugs": { 12 | "url": "https://github.com/notatestuser/gift/issues" 13 | }, 14 | "author": { 15 | "name": "sentientwaffle", 16 | "url": "http://sentientwaffle.github.com/" 17 | }, 18 | "main": "./lib/index", 19 | "scripts": { 20 | "test": "mocha --compilers coffee:'./node_modules/coffee-script/lib/coffee-script/coffee-script'", 21 | "prepublish": "coffee -o lib -c src" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/notatestuser/gift.git" 26 | }, 27 | "dependencies": { 28 | "underscore": "1.x.x" 29 | }, 30 | "devDependencies": { 31 | "should": "~2.0.1", 32 | "mocha": "1.x.x", 33 | "sinon": "~1.7.3", 34 | "coffee-script": "1.6.x", 35 | "rimraf": "2.0.x" 36 | }, 37 | "engines": { 38 | "node": "> 0.4.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/commit.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | fixtures = require './fixtures' 3 | git = require '../src' 4 | Commit = require '../src/commit' 5 | Tree = require '../src/tree' 6 | 7 | describe "Commit", -> 8 | describe "#tree", -> 9 | repo = git "#{__dirname}/fixtures/branched" 10 | tree = null 11 | before (done) -> 12 | repo.commits "master", (err, commits) -> 13 | tree = commits[0].tree() 14 | done err 15 | 16 | it "passes a tree", -> 17 | tree.should.be.an.instanceof Tree 18 | 19 | 20 | describe "#parents", -> 21 | repo = fixtures.branched 22 | parents = null 23 | parent = null 24 | before (done) -> 25 | repo.commits "something", (err, commits) -> 26 | parents = commits[0].parents() 27 | parent = commits[1] 28 | done err 29 | 30 | it "is an Array of Commits", -> 31 | parents.should.be.an.instanceof Array 32 | parents[0].should.be.an.instanceof Commit 33 | 34 | it "has the parent commit", -> 35 | parents[0].id.should.eql parent.id 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 [DJG](https://github.com/sentientwaffle) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files 5 | (the "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject 9 | to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/index.coffee: -------------------------------------------------------------------------------- 1 | {exec} = require 'child_process' 2 | Repo = require './repo' 3 | 4 | # Public: Create a Repo from the given path. 5 | # 6 | # Returns Repo. 7 | module.exports = Git = (path, bare=false) -> 8 | return new Repo path, bare 9 | 10 | 11 | # Public: Initialize a git repository. 12 | # 13 | # path - The directory to run `git init .` in. 14 | # bare - Create a bare repository when true. 15 | # callback - Receives `(err, repo)`. 16 | # 17 | Git.init = (path, bare, callback) -> 18 | [bare, callback] = [callback, bare] if !callback 19 | if bare 20 | bash = "git init --bare ." 21 | else 22 | bash = "git init ." 23 | exec bash, {cwd: path} 24 | , (err, stdout, stderr) -> 25 | return callback err if err 26 | return callback err, (new Repo path, bare) 27 | 28 | # Public: Clone a git repository. 29 | # 30 | # repository - The repository to clone from. 31 | # path - The directory to clone into. 32 | # callback - Receives `(err, repo)`. 33 | # 34 | Git.clone = (repository, path, callback) -> 35 | bash = "git clone #{repository} #{path}" 36 | exec bash, (err, stdout, stderr) -> 37 | return callback err if err 38 | return callback err, (new Repo path) -------------------------------------------------------------------------------- /test/config.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | Config = require '../src/config' 3 | 4 | GIT_CONFIG = """ 5 | user.name=John Doe 6 | user.email=john.doe@git-scm.com 7 | core.editor=pico 8 | """ 9 | 10 | GIT_CONFIG_DUPLICATE_KEYS = """ 11 | user.name=John Doe 12 | user.email=john.doe@git-scm.com 13 | core.editor=pico 14 | user.email=john.doe@github.com 15 | core.editor=emacs 16 | """ 17 | 18 | describe "Config", -> 19 | describe "()", -> 20 | describe "when there are no overlapping keys", -> 21 | config = new Config.Config 'mock repo' 22 | config.parse GIT_CONFIG 23 | 24 | it "read the keys and values", -> 25 | config.items['user.name'].should.equal 'John Doe' 26 | config.items['user.email'].should.equal 'john.doe@git-scm.com' 27 | config.items['core.editor'].should.equal 'pico' 28 | 29 | describe "with overlapping keys", -> 30 | config = new Config.Config 'mock repo' 31 | config.parse GIT_CONFIG_DUPLICATE_KEYS 32 | 33 | it "read the keys and values", -> 34 | config.items['user.name'].should.equal 'John Doe' 35 | config.items['user.email'].should.equal 'john.doe@github.com' 36 | config.items['core.editor'].should.equal 'emacs' 37 | -------------------------------------------------------------------------------- /test/fixtures/branched/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999490 -0700 commit (initial): 1 2 | b045ec4b14c60f03329654266478b6e8b92b33c5 b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999503 -0700 checkout: moving from master to something 3 | b045ec4b14c60f03329654266478b6e8b92b33c5 7783d64e077617ab7b84fe9726f721e921b9623b sentientwaffle 1328999518 -0700 commit: 2 4 | 7783d64e077617ab7b84fe9726f721e921b9623b b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1328999521 -0700 checkout: moving from something to master 5 | b045ec4b14c60f03329654266478b6e8b92b33c5 7783d64e077617ab7b84fe9726f721e921b9623b sentientwaffle 1329001555 -0700 checkout: moving from master to something 6 | 7783d64e077617ab7b84fe9726f721e921b9623b b045ec4b14c60f03329654266478b6e8b92b33c5 sentientwaffle 1329013635 -0700 checkout: moving from something to master 7 | b045ec4b14c60f03329654266478b6e8b92b33c5 913318e66e9beed3e89e9c402c1d6585ef3f7e6f sentientwaffle 1329013671 -0700 commit: add a sub dir 8 | -------------------------------------------------------------------------------- /test/tag.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | fixtures = require './fixtures' 3 | git = require '../src' 4 | Tag = require '../src/tag' 5 | 6 | describe "Tag", -> 7 | describe ".find_all", -> 8 | repo = fixtures.tagged 9 | tags = null 10 | before (done) -> 11 | Tag.find_all repo, (err, _tags) -> 12 | tags = _tags 13 | done err 14 | 15 | it "is an Array of Tags", -> 16 | tags.should.be.an.instanceof Array 17 | tags[0].should.be.an.instanceof Tag 18 | 19 | pref = "the tag" 20 | it "#{pref} has the correct name", -> 21 | tags[0].name.should.eql "tag-1" 22 | 23 | it "#{pref} has the correct commit", -> 24 | tags[0].commit.id.should.eql "32bbb351de16c3e404b3b7c77601c3d124e1e1a1" 25 | 26 | 27 | describe "#message", -> 28 | repo = fixtures.tagged 29 | tags = null 30 | message = null 31 | before (done) -> 32 | repo.tags (err, _tags) -> 33 | tags = _tags 34 | done err if err 35 | tags[0].message (err, _message) -> 36 | message = _message 37 | done err 38 | 39 | it "is the correct message", -> 40 | message.should.include "the first tag" 41 | 42 | it "has the correct commit", -> 43 | tags[0].commit.message.should.eql "commit 5" 44 | 45 | -------------------------------------------------------------------------------- /test/actor.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | Actor = require '../src/actor' 3 | 4 | describe "Actor", -> 5 | describe ".constructor", -> 6 | actor = new Actor "bob", "bob@example.com" 7 | it "assigns @name", -> 8 | actor.name.should.eql "bob" 9 | 10 | it "assigns @email", -> 11 | actor.email.should.eql "bob@example.com" 12 | 13 | 14 | describe "#toString", -> 15 | actor = new Actor "bob", "bob@example.com" 16 | 17 | it "is a string representation of the actor", -> 18 | actor.toString().should.eql "bob " 19 | 20 | 21 | describe "#hash", -> 22 | actor = new Actor "bob", "bob@example.com" 23 | 24 | it "is the md5 hash of the email", -> 25 | actor.hash.should.eql "4b9bb80620f03eb3719e0a061c14283d" 26 | 27 | 28 | describe ".from_string", -> 29 | describe "with a name and email", -> 30 | actor = Actor.from_string "bob " 31 | it "parses the name", -> 32 | actor.name.should.eql "bob" 33 | 34 | it "parses the email", -> 35 | actor.email.should.eql "bob@example.com" 36 | 37 | describe "with only a name", -> 38 | actor = Actor.from_string "bob" 39 | it "parses the name", -> 40 | actor.name.should.eql "bob" 41 | 42 | it "does not parse the email", -> 43 | should.not.exist actor.email 44 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by git-commit with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by git-commit with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by git-commit with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by git-commit with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by git-commit with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by git-commit with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by git-commit with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /src/ref.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | Commit = require './commit' 3 | 4 | exports.Ref = class Ref 5 | constructor: (@name, @commit) -> 6 | {@repo} = @commit 7 | 8 | # Public: Get a String representation of the Ref. 9 | toString: -> 10 | "#" 11 | 12 | # Internal: Find all refs. 13 | # 14 | # options - (optional). 15 | # 16 | # Returns Array of Ref. 17 | @find_all: (repo, type, RefClass, callback) -> 18 | repo.git.refs type, {}, (err, text) -> 19 | return callback err if err 20 | names = [] 21 | ids = [] 22 | for ref in text.split("\n") 23 | continue if !ref 24 | [name, id] = ref.split(' ') 25 | names.push name 26 | ids.push id 27 | 28 | Commit.find_commits repo, ids, (err, commits) -> 29 | return callback err if err 30 | refs = [] 31 | for name, i in names 32 | refs.push new RefClass name, commits[i] 33 | return callback null, refs 34 | 35 | 36 | exports.Head = class Head extends Ref 37 | @find_all: (repo, callback) -> 38 | Ref.find_all repo, "head", Head, callback 39 | 40 | @current: (repo, callback) -> 41 | fs.readFile "#{repo.dot_git}/HEAD", (err, data) -> 42 | return callback err if err 43 | [m, branch] = /ref: refs\/heads\/([^\s]+)/.exec data 44 | fs.readFile "#{repo.dot_git}/refs/heads/#{branch}", (err, id) -> 45 | Commit.find repo, id, (err, commit) -> 46 | return callback err if err 47 | return callback null, (new Head branch, commit) 48 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by git-commit with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git-rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ascii filenames set this variable to true. 19 | allownonascii=$(git config hooks.allownonascii) 20 | 21 | # Cross platform projects tend to avoid non-ascii filenames; prevent 22 | # them from being added to the repository. We exploit the fact that the 23 | # printable range starts at the space character and ends with tilde. 24 | if [ "$allownonascii" != "true" ] && 25 | # Note that the use of brackets around a tr range is ok here, (it's 26 | # even required, for portability to Solaris 10's /usr/bin/tr), since 27 | # the square bracket bytes happen to fall in the designated range. 28 | test "$(git diff --cached --name-only --diff-filter=A -z $against | 29 | LC_ALL=C tr -d '[ -~]\0')" 30 | then 31 | echo "Error: Attempt to add a non-ascii file name." 32 | echo 33 | echo "This can cause problems if you want to work" 34 | echo "with people on other platforms." 35 | echo 36 | echo "To be portable it is advisable to rename the file ..." 37 | echo 38 | echo "If you know what you are doing you can disable this" 39 | echo "check using:" 40 | echo 41 | echo " git config hooks.allownonascii true" 42 | echo 43 | exit 1 44 | fi 45 | 46 | exec git diff-index --check --cached $against -- 47 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by git-commit with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git-rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ascii filenames set this variable to true. 19 | allownonascii=$(git config hooks.allownonascii) 20 | 21 | # Cross platform projects tend to avoid non-ascii filenames; prevent 22 | # them from being added to the repository. We exploit the fact that the 23 | # printable range starts at the space character and ends with tilde. 24 | if [ "$allownonascii" != "true" ] && 25 | # Note that the use of brackets around a tr range is ok here, (it's 26 | # even required, for portability to Solaris 10's /usr/bin/tr), since 27 | # the square bracket bytes happen to fall in the designated range. 28 | test "$(git diff --cached --name-only --diff-filter=A -z $against | 29 | LC_ALL=C tr -d '[ -~]\0')" 30 | then 31 | echo "Error: Attempt to add a non-ascii file name." 32 | echo 33 | echo "This can cause problems if you want to work" 34 | echo "with people on other platforms." 35 | echo 36 | echo "To be portable it is advisable to rename the file ..." 37 | echo 38 | echo "If you know what you are doing you can disable this" 39 | echo "check using:" 40 | echo 41 | echo " git config hooks.allownonascii true" 42 | echo 43 | exit 1 44 | fi 45 | 46 | exec git diff-index --check --cached $against -- 47 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by git-commit with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git-rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ascii filenames set this variable to true. 19 | allownonascii=$(git config hooks.allownonascii) 20 | 21 | # Cross platform projects tend to avoid non-ascii filenames; prevent 22 | # them from being added to the repository. We exploit the fact that the 23 | # printable range starts at the space character and ends with tilde. 24 | if [ "$allownonascii" != "true" ] && 25 | # Note that the use of brackets around a tr range is ok here, (it's 26 | # even required, for portability to Solaris 10's /usr/bin/tr), since 27 | # the square bracket bytes happen to fall in the designated range. 28 | test "$(git diff --cached --name-only --diff-filter=A -z $against | 29 | LC_ALL=C tr -d '[ -~]\0')" 30 | then 31 | echo "Error: Attempt to add a non-ascii file name." 32 | echo 33 | echo "This can cause problems if you want to work" 34 | echo "with people on other platforms." 35 | echo 36 | echo "To be portable it is advisable to rename the file ..." 37 | echo 38 | echo "If you know what you are doing you can disable this" 39 | echo "check using:" 40 | echo 41 | echo " git config hooks.allownonascii true" 42 | echo 43 | exit 1 44 | fi 45 | 46 | exec git diff-index --check --cached $against -- 47 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by git-commit with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git-rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ascii filenames set this variable to true. 19 | allownonascii=$(git config hooks.allownonascii) 20 | 21 | # Cross platform projects tend to avoid non-ascii filenames; prevent 22 | # them from being added to the repository. We exploit the fact that the 23 | # printable range starts at the space character and ends with tilde. 24 | if [ "$allownonascii" != "true" ] && 25 | # Note that the use of brackets around a tr range is ok here, (it's 26 | # even required, for portability to Solaris 10's /usr/bin/tr), since 27 | # the square bracket bytes happen to fall in the designated range. 28 | test "$(git diff --cached --name-only --diff-filter=A -z $against | 29 | LC_ALL=C tr -d '[ -~]\0')" 30 | then 31 | echo "Error: Attempt to add a non-ascii file name." 32 | echo 33 | echo "This can cause problems if you want to work" 34 | echo "with people on other platforms." 35 | echo 36 | echo "To be portable it is advisable to rename the file ..." 37 | echo 38 | echo "If you know what you are doing you can disable this" 39 | echo "check using:" 40 | echo 41 | echo " git config hooks.allownonascii true" 42 | echo 43 | exit 1 44 | fi 45 | 46 | exec git diff-index --check --cached $against -- 47 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by git-commit with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git-rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ascii filenames set this variable to true. 19 | allownonascii=$(git config hooks.allownonascii) 20 | 21 | # Cross platform projects tend to avoid non-ascii filenames; prevent 22 | # them from being added to the repository. We exploit the fact that the 23 | # printable range starts at the space character and ends with tilde. 24 | if [ "$allownonascii" != "true" ] && 25 | # Note that the use of brackets around a tr range is ok here, (it's 26 | # even required, for portability to Solaris 10's /usr/bin/tr), since 27 | # the square bracket bytes happen to fall in the designated range. 28 | test "$(git diff --cached --name-only --diff-filter=A -z $against | 29 | LC_ALL=C tr -d '[ -~]\0')" 30 | then 31 | echo "Error: Attempt to add a non-ascii file name." 32 | echo 33 | echo "This can cause problems if you want to work" 34 | echo "with people on other platforms." 35 | echo 36 | echo "To be portable it is advisable to rename the file ..." 37 | echo 38 | echo "If you know what you are doing you can disable this" 39 | echo "check using:" 40 | echo 41 | echo " git config hooks.allownonascii true" 42 | echo 43 | exit 1 44 | fi 45 | 46 | exec git diff-index --check --cached $against -- 47 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by git-commit with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git-rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ascii filenames set this variable to true. 19 | allownonascii=$(git config hooks.allownonascii) 20 | 21 | # Cross platform projects tend to avoid non-ascii filenames; prevent 22 | # them from being added to the repository. We exploit the fact that the 23 | # printable range starts at the space character and ends with tilde. 24 | if [ "$allownonascii" != "true" ] && 25 | # Note that the use of brackets around a tr range is ok here, (it's 26 | # even required, for portability to Solaris 10's /usr/bin/tr), since 27 | # the square bracket bytes happen to fall in the designated range. 28 | test "$(git diff --cached --name-only --diff-filter=A -z $against | 29 | LC_ALL=C tr -d '[ -~]\0')" 30 | then 31 | echo "Error: Attempt to add a non-ascii file name." 32 | echo 33 | echo "This can cause problems if you want to work" 34 | echo "with people on other platforms." 35 | echo 36 | echo "To be portable it is advisable to rename the file ..." 37 | echo 38 | echo "If you know what you are doing you can disable this" 39 | echo "check using:" 40 | echo 41 | echo " git config hooks.allownonascii true" 42 | echo 43 | exit 1 44 | fi 45 | 46 | exec git diff-index --check --cached $against -- 47 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by git-commit with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git-rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ascii filenames set this variable to true. 19 | allownonascii=$(git config hooks.allownonascii) 20 | 21 | # Cross platform projects tend to avoid non-ascii filenames; prevent 22 | # them from being added to the repository. We exploit the fact that the 23 | # printable range starts at the space character and ends with tilde. 24 | if [ "$allownonascii" != "true" ] && 25 | # Note that the use of brackets around a tr range is ok here, (it's 26 | # even required, for portability to Solaris 10's /usr/bin/tr), since 27 | # the square bracket bytes happen to fall in the designated range. 28 | test "$(git diff --cached --name-only --diff-filter=A -z $against | 29 | LC_ALL=C tr -d '[ -~]\0')" 30 | then 31 | echo "Error: Attempt to add a non-ascii file name." 32 | echo 33 | echo "This can cause problems if you want to work" 34 | echo "with people on other platforms." 35 | echo 36 | echo "To be portable it is advisable to rename the file ..." 37 | echo 38 | echo "If you know what you are doing you can disable this" 39 | echo "check using:" 40 | echo 41 | echo " git config hooks.allownonascii true" 42 | echo 43 | exit 1 44 | fi 45 | 46 | exec git diff-index --check --cached $against -- 47 | -------------------------------------------------------------------------------- /src/tag.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | Commit = require './commit' 3 | Actor = require './actor' 4 | {Ref} = require './ref' 5 | 6 | module.exports = class Tag extends Ref 7 | @find_all: (repo, callback) -> 8 | Ref.find_all repo, "tag", Tag, callback 9 | 10 | 11 | # Public: Get the tag message. 12 | # 13 | # Returns String. 14 | message: (callback) -> 15 | @lazy (err, data) -> 16 | return callback err if err 17 | return callback null, data.message 18 | 19 | # Public: Get the tag author. 20 | # 21 | # Returns Actor. 22 | tagger: (callback) -> 23 | @lazy (err, data) -> 24 | return callback err if err 25 | return callback null, data.tagger 26 | 27 | # Public: Get the date that the tag was created. 28 | # 29 | # Returns Date. 30 | tag_date: (callback) -> 31 | @lazy (err, data) -> 32 | return callback err if err 33 | return callback null, data.tag_date 34 | 35 | # Internal: Load the tag data. 36 | lazy: (callback) -> 37 | return callback null, @_lazy_data if @_lazy_data 38 | @repo.git "cat-file", {}, ["tag", @name] 39 | , (err, stdout, stderr) => 40 | return callback err if err 41 | lines = stdout.split "\n" 42 | data = {} 43 | 44 | lines.shift() # object 4ae1cc5e6c7bb85b14ecdf221030c71d0654a42e 45 | lines.shift() # type commit 46 | lines.shift() # tag v0.0.2 47 | 48 | # bob 49 | author_line = lines.shift() 50 | [m, author, epoch] = /^.+? (.*) (\d+) .*$/.exec author_line 51 | 52 | data.tagger = Actor.from_string author 53 | data.tag_date = new Date epoch 54 | 55 | lines.shift() 56 | message = [] 57 | while line = lines.shift() 58 | message.push line 59 | data.message = message.join("\n") 60 | 61 | return callback null, (@_lazy_data = data) 62 | 63 | -------------------------------------------------------------------------------- /test/index.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | git = require '../src' 3 | Repo = require '../src/repo' 4 | fs = require "fs" 5 | {exec} = require 'child_process' 6 | 7 | describe "git", -> 8 | describe "()", -> 9 | repo = git "#{__dirname}/fixtures/simple" 10 | it "returns a Repo", -> 11 | repo.should.be.an.instanceof Repo 12 | 13 | describe "init()", -> 14 | repo = null 15 | newRepositoryDir = "#{__dirname}/fixtures/new" 16 | before (done) -> 17 | fs.mkdirSync newRepositoryDir 18 | git.init newRepositoryDir, (err, _repo) -> 19 | repo = _repo 20 | done err 21 | it "inits a Repo", -> 22 | repo.should.be.an.instanceof Repo 23 | bare = repo.bare || false 24 | bare.should.be.false 25 | after (done) -> 26 | exec "rm -rf #{newRepositoryDir}", done 27 | 28 | describe "init() bare", -> 29 | repo = null 30 | newRepositoryDir = "#{__dirname}/fixtures/bare" 31 | before (done) -> 32 | fs.mkdirSync newRepositoryDir 33 | git.init newRepositoryDir, true, (err, _repo) -> 34 | repo = _repo 35 | done err 36 | it "inits a bare Repo", -> 37 | repo.should.be.an.instanceof Repo 38 | bare = repo.bare || false 39 | bare.should.be.true 40 | after (done) -> 41 | exec "rm -rf #{newRepositoryDir}", done 42 | 43 | describe "clone()", -> 44 | @timeout 30000 45 | repo = null 46 | newRepositoryDir = "#{__dirname}/fixtures/clone" 47 | before (done) -> 48 | git.clone "https://github.com/notatestuser/gift.git", newRepositoryDir, (err, _repo) -> 49 | repo = _repo 50 | done err 51 | it "clone a repository", (done) -> 52 | repo.should.be.an.instanceof Repo 53 | repo.remote_list (err, remotes) -> 54 | remotes.should.have.length 1 55 | done() 56 | after (done) -> 57 | exec "rm -rf #{newRepositoryDir}", done 58 | -------------------------------------------------------------------------------- /src/submodule.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class Submodule 2 | constructor: (@repo, options) -> 3 | {@id, @name, @mode} = options 4 | 5 | # Public: Get the URL of the submodule. 6 | # 7 | # treeish - String treeish to look up the url within. 8 | # callback - Receives `(err, url)`. 9 | # 10 | url: (treeish, callback) -> 11 | [treeish, callback] = [callback, treeish] if !callback 12 | treeish ?= "master" 13 | 14 | Submodule.config @repo, treeish, (err, config) => 15 | return callback err, config?[@name].url 16 | 17 | 18 | # Internal: Parse the `.gitmodules` file. 19 | # 20 | # repo - A Repo. 21 | # treeish - String 22 | # callback - Receives `(err, config)`, where the config object has 23 | # the submodule names as its keys. 24 | # 25 | # Examples 26 | # 27 | # The following `.gitmodules` file: 28 | # 29 | # [submodule "spoon-knife"] 30 | # path = spoon-knife 31 | # url = git://github.com/octocat/Spoon-Knife.git 32 | # 33 | # would parse to: 34 | # 35 | # { "spoon-knife": 36 | # { "path": "spoon-knife" 37 | # , "url": "git://github.com/octocat/Spoon-Knife.git" 38 | # } 39 | # } 40 | # 41 | @config: (repo, treeish, callback) -> 42 | repo.tree(treeish).find ".gitmodules", (err, blob) -> 43 | return callback err if err 44 | blob.data (err, data) -> 45 | return callback err if err 46 | 47 | conf = {} 48 | lines = data.split "\n" 49 | current = null 50 | while lines.length 51 | line = lines.shift() 52 | 53 | if match = /^\[submodule "(.+)"\]$/.exec line 54 | current = match[1] 55 | conf[current] = {} 56 | else if match = /^\s+([^\s]+)\s+[=]\s+(.+)$/.exec line 57 | conf[current][match[1]] = match[2] 58 | 59 | return callback null, conf 60 | -------------------------------------------------------------------------------- /test/ref.test.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | should = require 'should' 3 | fixtures = require './fixtures' 4 | git = require '../src' 5 | Commit = require '../src/commit' 6 | 7 | {Ref, Head} = require '../src/ref' 8 | 9 | describe "Ref", -> 10 | describe ".find_all", -> 11 | describe "find remotes", -> 12 | repo = fixtures.remotes 13 | remotes = null 14 | before (done) -> 15 | Ref.find_all repo, "remote", Ref, (err, _remotes) -> 16 | remotes = _remotes 17 | done err 18 | 19 | it "is an Array of Refs", -> 20 | remotes.should.be.an.instanceof Array 21 | remotes[0].should.be.an.instanceof Ref 22 | 23 | it "the first item is a remote", -> 24 | remotes[0].name.should.eql "origin/HEAD" 25 | remotes[0].commit.should.be.an.instanceof Commit 26 | 27 | it "the second item is a remote", -> 28 | remotes[1].name.should.eql "origin/master" 29 | remotes[1].commit.should.be.an.instanceof Commit 30 | 31 | 32 | describe "Head", -> 33 | describe ".find_all", -> 34 | repo = fixtures.branched 35 | heads = null 36 | before (done) -> 37 | Head.find_all repo, (err, h) -> 38 | heads = h 39 | done err 40 | 41 | it "is an Array of Heads", -> 42 | heads.should.be.an.instanceof Array 43 | heads[0].should.be.an.instanceof Head 44 | 45 | it "contains the branches", -> 46 | heads.should.have.lengthOf 2 47 | names = _.map heads, ((b) -> b.name) 48 | names.should.include "master" 49 | names.should.include "something" 50 | 51 | 52 | describe ".current", -> 53 | repo = fixtures.branched 54 | branch = null 55 | before (done) -> 56 | Head.current repo, (err, b) -> 57 | branch = b 58 | done err 59 | 60 | it "is a Head", -> 61 | branch.should.be.an.instanceof Head 62 | 63 | it "has the correct name", -> 64 | branch.name.should.eql "master" 65 | -------------------------------------------------------------------------------- /test/status.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | fixtures = require './fixtures' 3 | git = require '../src' 4 | Status = require '../src/status' 5 | 6 | GIT_STATUS = """ 7 | M cheese.txt 8 | D crackers.txt 9 | M file.txt 10 | ?? pickles.txt 11 | """ 12 | GIT_STATUS_CLEAN = "" 13 | GIT_STATUS_NOT_CLEAN = """ 14 | A lib/index.js 15 | M npm-shrinkwrap.json 16 | M package.json 17 | """ 18 | 19 | describe "Status", -> 20 | describe "()", -> 21 | describe "when there are no changes", -> 22 | repo = fixtures.status 23 | status = new Status.Status repo 24 | status.parse GIT_STATUS_CLEAN 25 | 26 | it "is clean", -> 27 | status.clean.should.be.true 28 | 29 | describe "when there are changes", -> 30 | repo = fixtures.status 31 | status = new Status.Status repo 32 | status.parse GIT_STATUS_NOT_CLEAN 33 | it "is not clean", -> 34 | status.clean.should.be.false 35 | 36 | describe "when there are changes", -> 37 | repo = fixtures.status 38 | status = new Status.Status repo 39 | status.parse GIT_STATUS 40 | 41 | it "has a modified staged file", -> 42 | status.files["file.txt"].staged.should.be.true 43 | status.files["file.txt"].type.should.eql "M" 44 | status.files["file.txt"].tracked.should.be.true 45 | 46 | it "has a modified unstaged file", -> 47 | status.files["cheese.txt"].staged.should.be.false 48 | status.files["cheese.txt"].type.should.eql "M" 49 | status.files["cheese.txt"].tracked.should.be.true 50 | 51 | it "has a deleted file", -> 52 | status.files["crackers.txt"].staged.should.be.true 53 | status.files["crackers.txt"].type.should.eql "D" 54 | status.files["crackers.txt"].tracked.should.be.true 55 | 56 | it "has an untracked file", -> 57 | status.files["pickles.txt"].tracked.should.be.false 58 | should.not.exist status.files["pickles.txt"].type 59 | 60 | it "is not clean", -> 61 | status.clean.should.be.false 62 | -------------------------------------------------------------------------------- /test/blob.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | fixtures = require './fixtures' 3 | git = require '../src' 4 | Blob = require '../src/blob' 5 | 6 | describe "Blob", -> 7 | describe "constructor", -> 8 | repo = fixtures.branched 9 | blob = new Blob repo, {name: "X", mode: "Y", id: "abc"} 10 | it "assigns @name", -> 11 | blob.name.should.eql "X" 12 | it "assigns @mode", -> 13 | blob.mode.should.eql "Y" 14 | it "assigns @id", -> 15 | blob.id.should.eql "abc" 16 | 17 | 18 | describe "#data", -> 19 | describe "of a file off the root", -> 20 | repo = git "#{__dirname}/fixtures/branched" 21 | data = null 22 | before (done) -> 23 | repo.tree().blobs (err, blobs) -> 24 | blobs[0].data (err, _data) -> 25 | data = _data 26 | done err 27 | 28 | it "is a string", -> 29 | data.should.be.type "string" 30 | data.should.include "Bla" 31 | 32 | describe "of a file in a subdir", -> 33 | repo = git "#{__dirname}/fixtures/branched" 34 | data = null 35 | before (done) -> 36 | repo.tree().trees (err, trees) -> 37 | trees[0].blobs (err, blobs) -> 38 | blobs[0].data (err, _data) -> 39 | data = _data 40 | done err 41 | 42 | it "is a string", -> 43 | data.should.be.type "string" 44 | data.should.include "!!!" 45 | 46 | describe "#dataStream", -> 47 | describe "of a file off the root", -> 48 | repo = git "#{__dirname}/fixtures/branched" 49 | data = "" 50 | before (done) -> 51 | repo.tree().blobs (err, blobs) -> 52 | [dataStream, _] = blobs[0].dataStream() 53 | dataStream.on 'data', (buf) -> 54 | data += buf.toString() 55 | .on 'end', -> 56 | done() 57 | 58 | it "is a string", -> 59 | data.should.be.type "string" 60 | data.should.include "Bla" 61 | 62 | describe "of a file in a subdir", -> 63 | repo = git "#{__dirname}/fixtures/branched" 64 | data = "" 65 | before (done) -> 66 | repo.tree().trees (err, trees) -> 67 | trees[0].blobs (err, blobs) -> 68 | [dataStream, _] = blobs[0].dataStream() 69 | dataStream.on 'data', (buf) -> 70 | data += buf.toString() 71 | .on 'end', -> 72 | done() 73 | 74 | it "is a string", -> 75 | data.should.be.type "string" 76 | data.should.include "!!!" 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/git.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | {exec, spawn} = require 'child_process' 3 | 4 | module.exports = Git = (git_dir, dot_git) -> 5 | dot_git ||= "#{git_dir}/.git" 6 | 7 | git = (command, options, args, callback) -> 8 | [callback, args] = [args, callback] if !callback 9 | [callback, options] = [options, callback] if !callback 10 | options ?= {} 11 | options = options_to_argv options 12 | options = options.join " " 13 | args ?= [] 14 | args = args.join " " if args instanceof Array 15 | bash = "#{Git.bin} #{command} #{options} #{args}" 16 | exec bash, {cwd: git_dir, encoding:'binary'}, callback 17 | return bash 18 | 19 | # Public: Passthrough for raw git commands 20 | # 21 | git.cmd = (command, options, args, callback) -> 22 | git command, options, args, callback 23 | 24 | # Public: stream results of git command 25 | # 26 | # This is used for large files that you'd need to stream. 27 | # 28 | # returns [outstream, errstream] 29 | # 30 | git.streamCmd = (command, options, args) -> 31 | options ?= {} 32 | options = options_to_argv options 33 | args ?= [] 34 | allargs = [command].concat(options).concat(args) 35 | process = spawn Git.bin, allargs, {cwd: git_dir, encoding: 'binary'} 36 | return [process.stdout, process.stderr] 37 | 38 | # Public: Get a list of the remote names. 39 | # 40 | # callback - Receives `(err, names)`. 41 | # 42 | git.list_remotes = (callback) -> 43 | fs.readdir "#{dot_git}/refs/remotes", (err, files) -> 44 | callback err, (files || []) 45 | 46 | 47 | # Public: Get the ref data string. 48 | # 49 | # type - Such as `remote` or `tag`. 50 | # callback - Receives `(err, stdout)`. 51 | # 52 | git.refs = (type, options, callback) -> 53 | [callback, options] = [options, callback] if !callback 54 | prefix = "refs/#{type}s/" 55 | 56 | git "show-ref", (err, text) -> 57 | # ignore error code 1: means no match 58 | err = null if err?.code is 1 59 | matches = [] 60 | for line in (text || "").split("\n") 61 | continue if !line 62 | [id, name] = line.split(' ') 63 | if name.substr(0, prefix.length) == prefix 64 | matches.push "#{name.substr(prefix.length)} #{id}" 65 | return callback err, matches.join("\n") 66 | 67 | return git 68 | 69 | 70 | # Public: The `git` command. 71 | Git.bin = "git" 72 | 73 | 74 | 75 | # Internal: Transform an Object into command line options. 76 | # 77 | # Returns an Array of String option arguments. 78 | Git.options_to_argv = options_to_argv = (options) -> 79 | argv = [] 80 | for key, val of options 81 | if key.length == 1 82 | if val == true 83 | argv.push "-#{key}" 84 | else if val == false 85 | # ignore 86 | else 87 | argv.push "-#{key}" 88 | argv.push val 89 | else 90 | if val == true 91 | argv.push "--#{key}" 92 | else if val == false 93 | # ignore 94 | else 95 | argv.push "--#{key}=#{val}" 96 | return argv 97 | -------------------------------------------------------------------------------- /src/tree.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | Blob = require './blob' 3 | Submodule = require './submodule' 4 | 5 | module.exports = class Tree 6 | # repo - A Repo. 7 | # options - An Object with properties "id", "name", and "mode"; 8 | # or just a String id. 9 | constructor: (@repo, options) -> 10 | if _.isString options 11 | @id = options 12 | else 13 | {@id, @name, @mode} = options 14 | 15 | 16 | # Public: Get the children of the tree. 17 | # 18 | # callback - Receives `(err, children)`, where children is a list 19 | # of Trees, Blobs, and Submodules. 20 | # 21 | contents: (callback) -> 22 | return callback null, @_contents if @_contents 23 | @repo.git "ls-tree", {}, @id 24 | , (err, stdout, stderr) => 25 | return callback err if err 26 | @_contents = [] 27 | for line in stdout.split("\n") 28 | @_contents.push @content_from_string(line) if line 29 | return callback null, @_contents 30 | 31 | 32 | # Public: Get the child blobs. 33 | # 34 | # callback - Receives `(err, blobs)`. 35 | # 36 | blobs: (callback) -> 37 | @contents (err, children) -> 38 | return callback err if err 39 | return callback null, _.filter children, (child) -> 40 | child instanceof Blob 41 | 42 | 43 | # Public: Get the child blobs. 44 | # 45 | # callback - Receives `(err, trees)`. 46 | # 47 | trees: (callback) -> 48 | @contents (err, children) -> 49 | return callback err if err 50 | return callback null, _.filter children, (child) -> 51 | child instanceof Tree 52 | 53 | 54 | # Public: Find the named object in this tree's contents. 55 | # 56 | # callback - Receives `(err, obj)` where obj is Tree, Blob, or null 57 | # if not found. 58 | # 59 | find: (file, callback) -> 60 | if /\//.test file 61 | [dir, rest] = file.split "/", 2 62 | @trees (err, _trees) => 63 | for tree in _trees 64 | return tree.find rest, callback if tree.name == dir 65 | return callback null, null 66 | else 67 | @contents (err, children) -> 68 | return callback err if err 69 | for child in children 70 | if child.name == file 71 | return callback null, child 72 | return callback null, null 73 | 74 | 75 | # Internal: Parse a Blob or Tree from the line. 76 | # 77 | # line - String 78 | # 79 | # Examples 80 | # 81 | # tree.content_from_string "100644 blob e4ff69dd8f19d770e9731b4bc424ccb695f0b5ad README.md" 82 | # # => # 83 | # 84 | # Returns Blob, Tree or Submodule. 85 | content_from_string: (line) -> 86 | [mode, type, id, name] = line.split /[\t ]+/, 4 87 | switch type 88 | when "tree" 89 | new Tree @repo, {id, name, mode} 90 | when "blob" 91 | new Blob @repo, {id, name, mode} 92 | when "link" 93 | new Blob @repo, {id, name, mode} 94 | when "commit" 95 | new Submodule @repo, {id, name, mode} 96 | else 97 | throw new Error "Invalid object type: '#{type}'" 98 | 99 | # Public: Get a String representation of the Tree. 100 | # 101 | # Returns String. 102 | toString: -> 103 | "#" 104 | -------------------------------------------------------------------------------- /src/diff.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | Blob = require './blob' 3 | 4 | module.exports = class Diff 5 | constructor: (@repo, @a_path, @b_path, a_blob, b_blob 6 | , @a_mode, @b_mode, @new_file, @deleted_file, @diff 7 | , @renamed_file=false, @similarity_index=0) -> 8 | if a_blob isnt null 9 | @a_blob = new Blob @repo, {id: a_blob} 10 | @a_sha = a_blob 11 | if b_blob isnt null 12 | @b_blob = new Blob @repo, {id: b_blob} 13 | @b_sha = b_blob 14 | 15 | toJSON: -> 16 | {@a_path, @b_path, @a_mode, @b_mode, @new_file 17 | , @deleted_file, @diff, @renamed_file, @similarity_index} 18 | 19 | # Public: Parse the Diffs from the command output. 20 | # 21 | # text - String stdout of a `git diff` command. 22 | # 23 | # Returns Array of Diff. 24 | @parse: (repo, text) -> 25 | lines = text.split "\n" 26 | diffs = [] 27 | 28 | while lines.length && lines[0] 29 | # FIXME shift is O(n), so iterating n over O(n) operation might be O(n^2) 30 | [m, a_path, b_path] = ///^diff\s--git\s"?a/(.+?)"?\s"?b/(.+)"?$///.exec lines.shift() 31 | 32 | if /^old mode/.test lines[0] 33 | [m, a_mode] = /^old mode (\d+)/.exec lines.shift() 34 | [m, b_mode] = /^new mode (\d+)/.exec lines.shift() 35 | 36 | if !lines.length || /^diff --git/.test(lines[0]) 37 | diffs.push new Diff(repo, a_path, b_path, null, null, a_mode, b_mode, false, false, null) 38 | continue 39 | 40 | sim_index = 0 41 | new_file = false 42 | deleted_file = false 43 | renamed_file = false 44 | 45 | if /^new file/.test lines[0] 46 | [m, b_mode] = /^new file mode (.+)$/.exec lines.shift() 47 | a_mode = null 48 | new_file = true 49 | else if /^deleted file/.test lines[0] 50 | [m, a_mode] = /^deleted file mode (.+)$/.exec lines.shift() 51 | b_mode = null 52 | deleted_file = true 53 | else if m = /^similarity index (\d+)\%/.exec(lines[0]) 54 | sim_index = m[1].to_i 55 | renamed_file = true 56 | # shift away the 2 `rename from/to ...` lines 57 | lines.shift() 58 | lines.shift() 59 | 60 | [m, a_blob, b_blob, b_mode] = ///^index\s([0-9A-Fa-f]+)\.\.([0-9A-Fa-f]+)\s?(.+)?$///.exec lines.shift() 61 | b_mode = b_mode.trim() if b_mode 62 | 63 | diff_lines = [] 64 | while lines[0] && !/^diff/.test(lines[0]) 65 | diff_lines.push lines.shift() 66 | diff = diff_lines.join "\n" 67 | 68 | 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) 69 | 70 | return diffs 71 | 72 | # Public: Parse the raw diff format from the command output. 73 | # 74 | # text - String stdout of a `git diff` command. 75 | # 76 | # Returns Array of Diff. 77 | @parse_raw: (repo, text) -> 78 | lines = _.compact(text.split "\n") 79 | diffs = [] 80 | 81 | for line in lines 82 | line = line[1..-1] # get rid of leading ':' 83 | line = line.replace(/\.\.\./g, '') 84 | [a_mode, b_mode, a_sha, b_sha, status, a_path, b_path] = line.split(/\s/) 85 | b_path = a_path unless b_path 86 | new_file = status is 'M' 87 | deleted_file = status is 'D' 88 | renamed_file = status is 'R' 89 | 90 | diffs.push new Diff( 91 | repo, a_path, b_path, a_sha, b_sha, a_mode, b_mode, 92 | new_file, deleted_file, null, renamed_file, null 93 | ) 94 | 95 | return diffs 96 | -------------------------------------------------------------------------------- /src/commit.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | Actor = require './actor' 3 | Tree = require './tree' 4 | 5 | module.exports = class Commit 6 | constructor: (@repo, @id, parents, tree, @author, @authored_date, @committer, @committed_date, @message) -> 7 | # Public: Get the commit's Tree. 8 | # 9 | # Returns Tree. 10 | @tree = _.memoize => (new Tree @repo, tree) 11 | 12 | # Public: Get the Commit's parent Commits. 13 | # 14 | # Returns an Array of Commits. 15 | @parents = _.memoize => 16 | _.map parents, (parent) => 17 | new Commit @repo, parent 18 | 19 | 20 | toJSON: -> 21 | {@id, @author, @authored_date, @committer, @committed_date, @message} 22 | 23 | 24 | # Public: Find the matching commits. 25 | # 26 | # callback - Receives `(err, commits)` 27 | # 28 | @find_all: (repo, ref, options, callback) -> 29 | options = _.extend {pretty: "raw"}, options 30 | repo.git "rev-list", options, ref 31 | , (err, stdout, stderr) => 32 | return callback err if err 33 | return callback null, @parse_commits(repo, stdout) 34 | 35 | 36 | @find: (repo, id, callback) -> 37 | options = {pretty: "raw", "max-count": 1} 38 | repo.git "rev-list", options, id 39 | , (err, stdout, stderr) => 40 | return callback err if err 41 | return callback null, @parse_commits(repo, stdout)[0] 42 | 43 | 44 | @find_commits: (repo, ids, callback) -> 45 | commits = [] 46 | next = (i) -> 47 | if id = ids[i] 48 | Commit.find repo, id, (err, commit) -> 49 | return callback err if err 50 | commits.push commit 51 | next i + 1 52 | # Done: all commits loaded. 53 | else 54 | callback null, commits 55 | next 0 56 | 57 | 58 | # Internal: Parse the commits from `git rev-list` 59 | # 60 | # Return Commit[] 61 | @parse_commits: (repo, text) -> 62 | commits = [] 63 | lines = text.split "\n" 64 | while lines.length 65 | id = _.last lines.shift().split(" ") 66 | break if !id 67 | tree = _.last lines.shift().split(" ") 68 | 69 | parents = [] 70 | while /^parent/.test lines[0] 71 | parents.push _.last lines.shift().split(" ") 72 | 73 | author_line = lines.shift() 74 | if !/^committer /.test(lines[0]) 75 | author_line.push lines.shift() 76 | [author, authored_date] = @actor author_line 77 | 78 | committer_line = lines.shift() 79 | if lines[0] && !/^encoding/.test(lines[0]) 80 | committer_line.push lines.shift() 81 | [committer, committed_date] = @actor committer_line 82 | 83 | # not doing anything with this yet, but it's sometimes there 84 | if /^encoding/.test lines.first 85 | encoding = _.last lines.shift().split(" ") 86 | 87 | lines.shift() 88 | 89 | message_lines = [] 90 | while /^ {4}/.test lines[0] 91 | message_lines.push lines.shift()[4..-1] 92 | 93 | while lines[0]? && !lines[0].length 94 | lines.shift() 95 | 96 | commits.push new Commit(repo, id, parents, tree, author, authored_date, committer, committed_date, message_lines.join("\n")) 97 | return commits 98 | 99 | 100 | # Internal: Parse the actor. 101 | # 102 | # Returns [String name and email, Date] 103 | @actor: (line) -> 104 | [m, actor, epoch] = /^.+? (.*) (\d+) .*$/.exec line 105 | return [Actor.from_string(actor), new Date(1000 * +epoch)] 106 | -------------------------------------------------------------------------------- /test/tree.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | fixtures = require './fixtures' 3 | git = require '../src' 4 | Tree = require '../src/tree' 5 | Blob = require '../src/blob' 6 | Submodule = require '../src/submodule' 7 | 8 | describe "Tree", -> 9 | describe "#contents", -> 10 | describe "simple", -> 11 | repo = fixtures.branched 12 | tree = repo.tree() 13 | contents = null 14 | 15 | before (done) -> 16 | tree.contents (err, _contents) -> 17 | contents = _contents 18 | done err 19 | 20 | it "is an Array", -> 21 | contents.should.be.an.instanceof Array 22 | 23 | it "contains a Blob", -> 24 | contents[0].should.be.an.instanceof Blob 25 | contents[0].name.should.eql "README.md" 26 | 27 | it "contains a Tree", -> 28 | contents[1].should.be.an.instanceof Tree 29 | contents[1].name.should.eql "some" 30 | 31 | describe "with submodules", -> 32 | repo = fixtures.submodule 33 | tree = repo.tree() 34 | contents = null 35 | 36 | before (done) -> 37 | tree.contents (err, _contents) -> 38 | contents = _contents 39 | done err 40 | 41 | it "contains a Submodule", (done) -> 42 | contents[2].should.be.an.instanceof Submodule 43 | contents[2].name.should.eql "spoon-knife" 44 | contents[2].url (err, url) -> 45 | url.should.eql "git://github.com/octocat/Spoon-Knife.git" 46 | done err 47 | 48 | 49 | describe "#blobs", -> 50 | repo = fixtures.branched 51 | tree = repo.tree() 52 | blobs = null 53 | 54 | before (done) -> 55 | tree.blobs (err, _blobs) -> 56 | blobs = _blobs 57 | done err 58 | 59 | it "has only 1 item", -> 60 | blobs.should.have.lengthOf 1 61 | 62 | it "contains a Blob", -> 63 | blobs[0].should.be.an.instanceof Blob 64 | blobs[0].name.should.eql "README.md" 65 | 66 | 67 | describe "#trees", -> 68 | repo = fixtures.branched 69 | tree = repo.tree() 70 | trees = null 71 | 72 | before (done) -> 73 | tree.trees (err, _trees) -> 74 | trees = _trees 75 | done err 76 | 77 | it "has only 1 item", -> 78 | trees.should.have.lengthOf 1 79 | 80 | it "contains a Tree", -> 81 | trees[0].should.be.an.instanceof Tree 82 | trees[0].name.should.eql "some" 83 | 84 | 85 | describe "#find", -> 86 | repo = fixtures.branched 87 | tree = repo.tree() 88 | describe "find a file", -> 89 | blob = null 90 | before (done) -> 91 | tree.find "README.md", (err, _blob) -> 92 | blob = _blob 93 | done err 94 | 95 | it "finds the Blob", -> 96 | blob.should.be.an.instanceof Blob 97 | blob.name.should.eql "README.md" 98 | 99 | describe "find a directory", -> 100 | subtree = null 101 | before (done) -> 102 | tree.find "some", (err, _tree) -> 103 | subtree = _tree 104 | done err 105 | 106 | it "finds the Tree", -> 107 | subtree.should.be.an.instanceof Tree 108 | subtree.name.should.eql "some" 109 | 110 | describe "find inside a directory", -> 111 | blob = null 112 | before (done) -> 113 | tree.find "some/hi.txt", (err, _blob) -> 114 | blob = _blob 115 | done err 116 | 117 | it "finds the Blob", -> 118 | blob.should.be.an.instanceof Blob 119 | blob.name.should.eql "hi.txt" 120 | 121 | describe "find a nonexistant file", -> 122 | subtree = null 123 | before (done) -> 124 | tree.find "nonexistant", (err, _tree) -> 125 | subtree = _tree 126 | done err 127 | 128 | it "is null", -> 129 | should.not.exist subtree 130 | 131 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to blocks unannotated tags from entering. 4 | # Called by git-receive-pack with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "Usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git-cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to blocks unannotated tags from entering. 4 | # Called by git-receive-pack with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "Usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git-cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to blocks unannotated tags from entering. 4 | # Called by git-receive-pack with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "Usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git-cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to blocks unannotated tags from entering. 4 | # Called by git-receive-pack with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "Usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git-cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to blocks unannotated tags from entering. 4 | # Called by git-receive-pack with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "Usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git-cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to blocks unannotated tags from entering. 4 | # Called by git-receive-pack with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "Usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git-cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to blocks unannotated tags from entering. 4 | # Called by git-receive-pack with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "Usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git-cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /test/diff.test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | fixtures = require './fixtures' 3 | git = require '../src' 4 | Diff = require '../src/diff' 5 | Blob = require '../src/blob' 6 | 7 | 8 | describe "Diff", -> 9 | describe ".parse", -> 10 | describe "simple editing", -> 11 | repo = fixtures.tagged 12 | stdout = """ 13 | diff --git a/file.txt b/file.txt 14 | index d00491f..48082f7 100644 15 | --- a/file.txt 16 | +++ b/file.txt 17 | @@ -1 +1 @@ 18 | -1 19 | +12 20 | """ 21 | diffs = Diff.parse repo, stdout 22 | 23 | it "is an Array of Diffs", -> 24 | diffs.should.be.an.instanceof Array 25 | diffs[0].should.be.an.instanceof Diff 26 | 27 | it "has one diff", -> 28 | diffs.should.have.lengthOf 1 29 | 30 | describe "the first diff", -> 31 | diff = diffs[0] 32 | 33 | it "has the repo", -> 34 | diff.repo.should.eql repo 35 | 36 | for blob in ["a_blob", "b_blob"] 37 | it "has a #{blob}", -> 38 | diff[blob].should.be.an.instanceof Blob 39 | 40 | for path in ["a_path", "b_path"] 41 | it "has a #{path}", -> 42 | diff[path].should.eql "file.txt" 43 | 44 | it "has a b_mode", -> 45 | diff.b_mode.should.eql "100644" 46 | 47 | for change in ["new_file", "renamed_file", "deleted_file"] 48 | it "#{change} is false", -> 49 | diff[change].should.be.false 50 | 51 | it "has a similarity_index of 0", -> 52 | diff.similarity_index.should.eql 0 53 | 54 | describe ".parse_raw", -> 55 | describe "simple editing", -> 56 | repo = fixtures.tagged 57 | stdout = """ 58 | :100644 100644 95f6539... 0466f13... M file.txt 59 | """ 60 | diffs = Diff.parse_raw repo, stdout 61 | 62 | it "is an Array of Diffs", -> 63 | diffs.should.be.an.instanceof Array 64 | diffs[0].should.be.an.instanceof Diff 65 | 66 | it "has one diff", -> 67 | diffs.should.have.lengthOf 1 68 | 69 | describe "the first diff", -> 70 | diff = diffs[0] 71 | 72 | it "has the repo", -> 73 | diff.repo.should.eql repo 74 | 75 | for blob in ["a_blob", "b_blob"] 76 | it "has a #{blob}", -> 77 | diff[blob].should.be.an.instanceof Blob 78 | 79 | for path in ["a_path", "b_path"] 80 | it "has a #{path}", -> 81 | diff[path].should.eql "file.txt" 82 | 83 | it "has a b_mode", -> 84 | diff.b_mode.should.eql "100644" 85 | 86 | for change in ["new_file", "renamed_file", "deleted_file"] 87 | it "#{change} is false", -> 88 | diff[change].should.be.false 89 | 90 | it "has a similarity_index of 0", -> 91 | diff.similarity_index.should.eql 0 92 | 93 | describe "delete a file", -> 94 | repo = fixtures.branched 95 | stdout = """ 96 | diff --git a/README.md b/README.md 97 | index e4ff69d..c0efd1c 100644 98 | --- a/README.md 99 | +++ b/README.md 100 | @@ -1 +1 @@ 101 | -Bla 102 | +Bla2 103 | diff --git a/some/hi.txt b/some/hi.txt 104 | deleted file mode 100644 105 | index 6f1de24..0000000 106 | --- a/some/hi.txt 107 | +++ /dev/null 108 | @@ -1 +0,0 @@ 109 | -!!! 110 | """ 111 | diffs = Diff.parse repo, stdout 112 | 113 | it "has 2 diffs", -> 114 | diffs.should.have.lengthOf 2 115 | 116 | describe "the second diff", -> 117 | diff = diffs[1] 118 | it "deletes a file", -> 119 | diff.deleted_file.should.be.true 120 | 121 | describe "create a file", -> 122 | repo = fixtures.branched 123 | stdout = """ 124 | diff --git a/some/hi.txt b/some/hi.txt 125 | new file mode 100644 126 | index 0000000..6f1de24 127 | --- /dev/null 128 | +++ b/some/hi.txt 129 | @@ -0,0 +1 @@ 130 | +!!! 131 | """ 132 | diffs = Diff.parse repo, stdout 133 | 134 | it "creates a file", -> 135 | diffs[0].new_file.should.be.true 136 | 137 | 138 | -------------------------------------------------------------------------------- /test/fixtures/remotes/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git-rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git-rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git-rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` 68 | perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git-rev-list ^master ^topic next 161 | git-rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git-rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /test/fixtures/simple/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git-rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git-rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git-rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` 68 | perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git-rev-list ^master ^topic next 161 | git-rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git-rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /test/fixtures/status/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git-rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git-rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git-rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` 68 | perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git-rev-list ^master ^topic next 161 | git-rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git-rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /test/fixtures/tagged/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git-rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git-rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git-rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` 68 | perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git-rev-list ^master ^topic next 161 | git-rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git-rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /test/fixtures/branched/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git-rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git-rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git-rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` 68 | perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git-rev-list ^master ^topic next 161 | git-rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git-rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /test/fixtures/checkout/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git-rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git-rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git-rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` 68 | perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git-rev-list ^master ^topic next 161 | git-rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git-rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /test/fixtures/submodule/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git-rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git-rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git-rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` 68 | perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git-rev-list ^master ^topic next 161 | git-rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git-rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gift [![Build Status](https://secure.travis-ci.org/notatestuser/gift.png?branch=master)](http://travis-ci.org/notatestuser/gift) [![Dependency Status](https://david-dm.org/notatestuser/gift.png)](https://david-dm.org/notatestuser/gift) 2 | 3 | A simple Node.js wrapper for the Git CLI. The API is based on 4 | [Grit](https://github.com/mojombo/grit) 5 | 6 | # Installation 7 | 8 | This fork is now in the `npm` package repository. Install it like you would any other package: 9 | 10 | $ npm install gift 11 | 12 | # API 13 | 14 | For existing repositories: 15 | 16 | git = require 'gift' 17 | 18 | repo = git "path/to/repo" 19 | # => # 20 | 21 | Initialize a new repository: 22 | 23 | git = require 'gift' 24 | 25 | git.init "path/to/repo", (err, _repo) -> 26 | repo = _repo 27 | # => # 28 | 29 | Initialize a new bare repository: 30 | 31 | git = require 'gift' 32 | 33 | git.init "path/to/bare/repo", true, (err, _repo) -> 34 | repo = _repo 35 | # => # 36 | 37 | Clone a repository: 38 | 39 | git = require 'gift' 40 | 41 | git.clone "git@host:path/to/remote/repo.git", "path/to/local/clone/repo", (err, _repo) -> 42 | repo = _repo 43 | # => # 44 | 45 | ## Repo 46 | ### `Repo#path` 47 | `String` - The path to the repository. 48 | 49 | ### `Repo#commits([treeish, [limit, [skip, ]]]callback)` 50 | Get a list of commits. 51 | 52 | * `treeish` - `String` (optional). 53 | * `limit` - `Integer` (optional). 54 | * `skip` - `Integer` (optional). 55 | * `callback` - `Function` which receives `(err, commits)`, where `commits` is 56 | an `Array` of `Commit`s. 57 | 58 | Get the 10 most recent commits to master. 59 | 60 | repo.commits (err, commits) -> 61 | 62 | Or to a different tag or branch. 63 | 64 | repo.commits "v0.0.3", (err, commits) -> 65 | 66 | Limit the maximum number of commits returned (by default limit is 10). 67 | 68 | repo.commits "master", 30, (err, commits) -> 69 | 70 | Skip some (for pagination): 71 | 72 | repo.commits "master", 30, 30, (err, commits) -> 73 | 74 | Or get an unlimited number of commits (there could be a lot): 75 | 76 | repo.commits "master", -1, (err, commits) -> 77 | 78 | ### `Repo#current_commit(callback)` 79 | Get the current commit. 80 | 81 | The callback receives `(err, commit)`. 82 | 83 | ### `Repo#tree([treeish]) => Tree` 84 | The `Tree` object for the treeish (which defaults to "master"). 85 | 86 | repo.tree().contents (err, children) -> 87 | for child in children 88 | console.log child.name 89 | 90 | ### `Repo#diff(commitA, commitB, [paths, ]callback)` 91 | Get the difference between the trees. 92 | 93 | The callback receives `(err, diffs)`. 94 | 95 | ### `Repo#identity(callback)` 96 | Get the commit identity for this repository. 97 | 98 | The callback receives `(err, actor)`, where `actor` is an Actor. 99 | 100 | ### `Repo#identify(actor, callback)` 101 | Set your account's default identity for commits to this repository. 102 | 103 | The callback receives `(err)`. 104 | 105 | ### `Repo#remotes(callback)` 106 | Get the repository's remotes. 107 | 108 | Receives `(err, remotes)`, where each remote is a Ref. 109 | 110 | ### `Repo#remote_list(callback)` 111 | Get a list of the repository's remote names. 112 | 113 | Get the string names of each of the remotes. 114 | 115 | ### `Repo#remote_add(name, url, callback)` 116 | Equivalent to `git remote add `. 117 | 118 | ### `Repo#remote_remove(name, callback)` 119 | Remove a remote. 120 | 121 | ### `Repo#remote_fetch(name, callback)` 122 | `git fetch ` 123 | 124 | ### `Repo#remote_push(name, callback)` 125 | `git push ` 126 | 127 | ### `Repo#status(callback)` 128 | Uses `--porcelain` to parse repository status in a way that is agnostic of system language. The callback receives `(err, status)`. See below for a definition of what `status` is. 129 | 130 | ### `Repo#config(callback)` 131 | `git config` parsed as a simple, one-level object. The callback receives `(err, config)`. 132 | 133 | ### `Repo#create_branch(name, callback)` 134 | Create a new branch with `name`, and call the callback when complete 135 | with an error, if one occurred. 136 | 137 | ### `Repo#delete_branch(name, callback)` 138 | Delete the branch `name`, and call the callback with an error, if one occurred. 139 | 140 | ### `Repo#tags(callback)` 141 | Get a list of `Tag`s. 142 | 143 | ### `Repo#create_tag(name, [options, ]callback)` 144 | Create a tab with the given name. 145 | 146 | ### `Repo#delete_tag(name, callback)` 147 | Delete the tag with the given name. 148 | 149 | ### `Repo#branches(callback)` 150 | `callback` receives `(err, heads)`. 151 | 152 | ### `Repo#create_branch(name, callback)` 153 | Create a branch with the given name. 154 | 155 | ### `Repo#delete_branch(delete, callback)` 156 | Delete the branch with the given name. 157 | 158 | ### `Repo#branch([branch, ]callback)` 159 | Get a branch. 160 | 161 | * `branch` - The name of the branch to get. Defaults to the 162 | currently checked out branch. 163 | * `callback` - Receives `(err, head)`. 164 | 165 | 166 | ### `Repo#commit(message, [options, ]callback)` 167 | Commit some changes. 168 | 169 | * `message` - `String` 170 | * `options` - 171 | - `all` - `Boolean` 172 | - `amend` - `Boolean` 173 | - `author` - `String` that must match "Au thor Author " 174 | * `callback` - Receives `(err)`. 175 | 176 | ### `Repo#add(files, callback)` 177 | `git add ` 178 | 179 | ### `Repo#remove(files, callback)` 180 | `git rm ` 181 | 182 | ### `Repo#checkout(treeish, callback)` 183 | `git checkout ` 184 | 185 | ### `Repo#sync([[remote, ]branch, ]callback)` 186 | Sync the current branch with the remote, keeping all local changes intact. 187 | 188 | The following steps are carried out: `stash`, `pull`, `push`, `stash pop`. If there were no changes to stash, the last `stash pop` is not executed. 189 | 190 | * `remote` - `String` (defaults to `origin`). 191 | * `branch` - `String` (defaults to `master`). 192 | * `callback` - Receives `(err)`. 193 | 194 | 195 | ## Commit 196 | ### `Commit#id` 197 | `String` - The commit's SHA. 198 | 199 | ### `Commit#parents` 200 | `Commit[]` 201 | 202 | ### `Commit#tree()` 203 | `Tree` - The commit's content tree. 204 | 205 | ### `Commit#author` 206 | `Actor` 207 | ### `Commit#authored_date` 208 | `Date` 209 | ### `Commit#committer` 210 | `Actor` 211 | ### `Commit#committed_date` 212 | `Date` 213 | ### `Commit#message` 214 | `String` 215 | 216 | 217 | ## Head 218 | ### `Head#name` 219 | `String` 220 | 221 | ### `Head#commit` 222 | `Commit` 223 | 224 | ## Tag 225 | ### `Tag#name` 226 | `String` 227 | 228 | ### `Tag#commit` 229 | `Commit` 230 | 231 | ### `Tag#message(callback)` 232 | The callback receives `(err, message)` (`message` is a String). 233 | 234 | ### `Tag#tagger(callback)` 235 | The callback receives `(err, actor)`. 236 | 237 | ### `Tag#tag_date(callback)` 238 | The callback receives `(err, date)`. 239 | 240 | ## Config 241 | ### `Config#items` 242 | `Object` - The keys are dotted precisely as the console output from `git config`. E.g., `{'user.name': 'John Doe'}` 243 | 244 | ## Status 245 | ### `Status#clean` 246 | `Boolean` 247 | 248 | ### `Status#files` 249 | `Object` - The keys are files, the values objects indicating whether or not 250 | the file is staged, tracked, etc. 251 | 252 | Each file has the following properties: 253 | 254 | * `type` - "A" for added, "M" for modified, "D" for deleted. 255 | * `staged` - `Boolean` 256 | * `tracked` - `Boolean` 257 | 258 | ## Actor 259 | ### `Actor#name` 260 | `String` 261 | 262 | ### `Actor#email` 263 | `String` 264 | 265 | ### `Actor#hash` 266 | `String` - The MD5 hash of the actor's email. Useful for displaying 267 | [Gravatar](http://en.gravatar.com/) avatars. 268 | 269 | 270 | ## Tree 271 | ### `Tree#id` 272 | `String` - SHA1 273 | 274 | ### `Tree#contents(callback)` 275 | 276 | * `callback` - Receives `(err, children)`. 277 | * `children` - An array of `Blob`s, `Tree`s, and `Submodule`s. 278 | 279 | ### `Tree#blobs(callback)` 280 | 281 | * `callback` - Receives `(err, child_blobs)`. 282 | * `children` - `[Blob]` 283 | 284 | ### `Tree#trees(callback)` 285 | 286 | * `callback` - Receives `(err, child_trees)`. 287 | * `children` - `[Tree]` 288 | 289 | ### `Tree#find(directory, callback)` 290 | 291 | * `directory` - `String` 292 | * `callback` - Receives `(err, thing)`. 293 | 294 | ## Blob 295 | ### `Blob#id` 296 | `String` - SHA1 297 | 298 | ### `Blob#mode` 299 | `String` 300 | 301 | ### `Blob#data(callback)` 302 | 303 | * `callback` - `(err, data)` 304 | 305 | Warning: this method only returns the complete file up to 200k, which is the default 306 | buffer size for running child_process.exec(). If the file you're reading is bigger than 307 | that, or if you're not sure, you need to use dataStream() 308 | 309 | ### `Blob#dataStream()` 310 | 311 | * returns - [dataStream, errorStream] 312 | 313 | Returns streams for you to use to get the data. 314 | 315 | Usage: 316 | 317 | data = "" 318 | [dataStream, _] = blob.dataStream() 319 | dataStream.on 'data', (buf) -> 320 | data += buf.toString() 321 | .on 'end', -> 322 | callback(data) 323 | 324 | ## Submodule 325 | ### `Submodule#id` 326 | `String` 327 | 328 | ### `Submodule#name` 329 | `String` 330 | 331 | ### `Submodule#mode` 332 | `String` 333 | 334 | ### `Submodule#url(callback)` 335 | Get the url the submodule points to. 336 | 337 | 338 | # License 339 | See LICENSE. 340 | --------------------------------------------------------------------------------