Add sideband support for push
diff --git a/options.go b/options.go
index 0ec18d4..98b1194 100644
--- a/options.go
+++ b/options.go
@@ -160,6 +160,9 @@
 	RefSpecs []config.RefSpec
 	// Auth credentials, if required, to use with the remote repository.
 	Auth transport.AuthMethod
+	// Progress is where the human readable information sent by the server is
+	// stored, if nil nothing is stored.
+	Progress sideband.Progress
 }
 
 // Validate validates the fields and sets the default values.
diff --git a/plumbing/protocol/packp/updreq.go b/plumbing/protocol/packp/updreq.go
index b246613..73be117 100644
--- a/plumbing/protocol/packp/updreq.go
+++ b/plumbing/protocol/packp/updreq.go
@@ -6,6 +6,7 @@
 
 	"gopkg.in/src-d/go-git.v4/plumbing"
 	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
+	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
 )
 
 var (
@@ -21,6 +22,9 @@
 	Shallow      *plumbing.Hash
 	// Packfile contains an optional packfile reader.
 	Packfile io.ReadCloser
+
+	// Progress receives sideband progress messages from the server
+	Progress sideband.Progress
 }
 
 // New returns a pointer to a new ReferenceUpdateRequest value.
diff --git a/plumbing/transport/http/receive_pack.go b/plumbing/transport/http/receive_pack.go
index b54b70f..d2dfeb7 100644
--- a/plumbing/transport/http/receive_pack.go
+++ b/plumbing/transport/http/receive_pack.go
@@ -9,6 +9,8 @@
 
 	"gopkg.in/src-d/go-git.v4/plumbing"
 	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
+	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
+	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
 	"gopkg.in/src-d/go-git.v4/plumbing/transport"
 	"gopkg.in/src-d/go-git.v4/utils/ioutil"
 )
@@ -52,6 +54,17 @@
 		return nil, err
 	}
 
+	var d *sideband.Demuxer
+	if req.Capabilities.Supports(capability.Sideband64k) {
+		d = sideband.NewDemuxer(sideband.Sideband64k, r)
+	} else if req.Capabilities.Supports(capability.Sideband) {
+		d = sideband.NewDemuxer(sideband.Sideband, r)
+	}
+	if d != nil {
+		d.Progress = req.Progress
+		r = d
+	}
+
 	rc := ioutil.NewReadCloser(r, res.Body)
 
 	report := packp.NewReportStatus()
diff --git a/plumbing/transport/internal/common/common.go b/plumbing/transport/internal/common/common.go
index 2db8d54..598c6b1 100644
--- a/plumbing/transport/internal/common/common.go
+++ b/plumbing/transport/internal/common/common.go
@@ -18,6 +18,7 @@
 	"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
 	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
 	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
+	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
 	"gopkg.in/src-d/go-git.v4/plumbing/transport"
 	"gopkg.in/src-d/go-git.v4/utils/ioutil"
 )
@@ -298,13 +299,26 @@
 	}
 
 	if !req.Capabilities.Supports(capability.ReportStatus) {
-		// If we have neither report-status or sideband, we can only
+		// If we don't have report-status, we can only
 		// check return value error.
 		return nil, s.Command.Close()
 	}
 
+	r := s.StdoutContext(ctx)
+
+	var d *sideband.Demuxer
+	if req.Capabilities.Supports(capability.Sideband64k) {
+		d = sideband.NewDemuxer(sideband.Sideband64k, r)
+	} else if req.Capabilities.Supports(capability.Sideband) {
+		d = sideband.NewDemuxer(sideband.Sideband, r)
+	}
+	if d != nil {
+		d.Progress = req.Progress
+		r = d
+	}
+
 	report := packp.NewReportStatus()
-	if err := report.Decode(s.StdoutContext(ctx)); err != nil {
+	if err := report.Decode(r); err != nil {
 		return nil, err
 	}
 
diff --git a/remote.go b/remote.go
index c07c5af..1c9d1cd 100644
--- a/remote.go
+++ b/remote.go
@@ -65,7 +65,6 @@
 // operation is complete, an error is returned. The context only affects to the
 // transport operations.
 func (r *Remote) PushContext(ctx context.Context, o *PushOptions) error {
-	// TODO: Sideband support
 	if err := o.Validate(); err != nil {
 		return err
 	}
@@ -108,9 +107,8 @@
 		return ErrDeleteRefNotSupported
 	}
 
-	req := packp.NewReferenceUpdateRequestFromCapabilities(ar.Capabilities)
-	if err := r.addReferencesToUpdate(o.RefSpecs, remoteRefs, req); err != nil {
-
+	req, err := r.newReferenceUpdateRequest(o, remoteRefs, ar)
+	if err != nil {
 		return err
 	}
 
@@ -158,6 +156,25 @@
 	return r.updateRemoteReferenceStorage(req, rs)
 }
 
+func (r *Remote) newReferenceUpdateRequest(o *PushOptions, remoteRefs storer.ReferenceStorer, ar *packp.AdvRefs) (*packp.ReferenceUpdateRequest, error) {
+	req := packp.NewReferenceUpdateRequestFromCapabilities(ar.Capabilities)
+
+	if o.Progress != nil {
+		req.Progress = o.Progress
+		if ar.Capabilities.Supports(capability.Sideband64k) {
+			req.Capabilities.Set(capability.Sideband64k)
+		} else if ar.Capabilities.Supports(capability.Sideband) {
+			req.Capabilities.Set(capability.Sideband)
+		}
+	}
+
+	if err := r.addReferencesToUpdate(o.RefSpecs, remoteRefs, req); err != nil {
+		return nil, err
+	}
+
+	return req, nil
+}
+
 func (r *Remote) updateRemoteReferenceStorage(
 	req *packp.ReferenceUpdateRequest,
 	result *packp.ReportStatus,
diff --git a/repository_test.go b/repository_test.go
index e944251..6184949 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -719,6 +719,47 @@
 	c.Assert(err, NotNil)
 }
 
+// installPreReceiveHook installs a pre-receive hook in the .git
+// directory at path which prints message m before exiting
+// successfully.
+func installPreReceiveHook(c *C, path, m string) {
+	hooks := filepath.Join(path, "hooks")
+	err := os.MkdirAll(hooks, 0777)
+	c.Assert(err, IsNil)
+
+	err = ioutil.WriteFile(filepath.Join(hooks, "pre-receive"), preReceiveHook(m), 0777)
+	c.Assert(err, IsNil)
+}
+
+func (s *RepositorySuite) TestPushWithProgress(c *C) {
+	url := c.MkDir()
+	server, err := PlainInit(url, true)
+	c.Assert(err, IsNil)
+
+	m := "Receiving..."
+	installPreReceiveHook(c, url, m)
+
+	_, err = s.Repository.CreateRemote(&config.RemoteConfig{
+		Name: "bar",
+		URLs: []string{url},
+	})
+	c.Assert(err, IsNil)
+
+	var p bytes.Buffer
+	err = s.Repository.Push(&PushOptions{
+		RemoteName: "bar",
+		Progress:   &p,
+	})
+	c.Assert(err, IsNil)
+
+	AssertReferences(c, server, map[string]string{
+		"refs/heads/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
+		"refs/heads/branch": "e8d3ffab552895c19b9fcf7aa264d277cde33881",
+	})
+
+	c.Assert((&p).Bytes(), DeepEquals, []byte(m))
+}
+
 func (s *RepositorySuite) TestPushDepth(c *C) {
 	url := c.MkDir()
 	server, err := PlainClone(url, true, &CloneOptions{
diff --git a/repository_unix_test.go b/repository_unix_test.go
new file mode 100644
index 0000000..75682ae
--- /dev/null
+++ b/repository_unix_test.go
@@ -0,0 +1,11 @@
+// +build !plan9,!windows
+
+package git
+
+import "fmt"
+
+// preReceiveHook returns the bytes of a pre-receive hook script
+// that prints m before exiting successfully
+func preReceiveHook(m string) []byte {
+	return []byte(fmt.Sprintf("#!/bin/sh\nprintf '%s'\n", m))
+}
diff --git a/repository_windows_test.go b/repository_windows_test.go
new file mode 100644
index 0000000..bec0acd
--- /dev/null
+++ b/repository_windows_test.go
@@ -0,0 +1,9 @@
+package git
+
+import "fmt"
+
+// preReceiveHook returns the bytes of a pre-receive hook script
+// that prints m before exiting successfully
+func preReceiveHook(m string) []byte {
+	return []byte(fmt.Sprintf("#!C:/Program\\ Files/Git/usr/bin/sh.exe\nprintf '%s'\n", m))
+}