Add support for +describe

Change-Id: Ide90f4a77822184c4b3b2873ea1dad71ea5b62b5
diff --git a/gitiles/client.go b/gitiles/client.go
index de60c5c..7fec489 100644
--- a/gitiles/client.go
+++ b/gitiles/client.go
@@ -15,6 +15,10 @@
 // Package gitiles is a client library for the Gitiles source viewer.
 package gitiles
 
+// The gitiles command set is defined here:
+//
+// https://gerrit.googlesource.com/gitiles/+/7c07a4a68ece6009909206482e0728dbbf0be77d/java/com/google/gitiles/ViewFilter.java#47
+
 import (
 	"bytes"
 	"encoding/base64"
@@ -321,3 +325,43 @@
 	err := s.service.getJSON(&jsonURL, &c)
 	return &c, err
 }
+
+// Options for Describe.
+const (
+	// Return a ref that contains said commmit
+	DescribeContains = "contains"
+
+	// Return any type of ref
+	DescribeAll = "all"
+
+	// Only return a tag ref
+	DescribeTags = "tags"
+
+	// The default for 'contains': return annotated tags
+	DescribeAnnotatedTags = ""
+)
+
+// Describe describes a possibly shortened commit hash as a ref that
+// is visible to the caller. Currently, only the 'contains' flavor is
+// implemented, so options must always include 'contains'.
+func (s *RepoService) Describe(revision string, options ...string) (string, error) {
+	jsonURL := s.service.addr
+	jsonURL.Path = path.Join(jsonURL.Path, s.Name, "+describe", revision)
+	jsonURL.RawQuery = "format=JSON&" + strings.Join(options, "&")
+
+	result := map[string]string{}
+	err := s.service.getJSON(&jsonURL, &result)
+	if err != nil {
+		return "", err
+	}
+
+	if len(result) != 1 {
+		return "", fmt.Errorf("gitiles: got map %v, want just one entry", result)
+	}
+
+	for _, v := range result {
+		return v, nil
+	}
+
+	panic("unreachable.")
+}
diff --git a/gitiles/prod_test.go b/gitiles/prod_test.go
index 8ee9f87..c4fc68a 100644
--- a/gitiles/prod_test.go
+++ b/gitiles/prod_test.go
@@ -59,3 +59,25 @@
 		t.Fatal("did not find 'mux.go', got %v", names)
 	}
 }
+
+func TestProductionDescribe(t *testing.T) {
+	gs, err := NewService(Options{
+		Address: "https://gerrit.googlesource.com",
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	repo := gs.NewRepoService("gitiles")
+	if err != nil {
+		t.Fatal(err)
+	}
+	got, err := repo.Describe("9de65953ec", DescribeContains)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if want := "v0.1-6~361"; want != got {
+		t.Fatalf("got %v, want %v", got, want)
+	}
+}