diff --git a/github/git_trees.go b/github/git_trees.go index df843f4de9a..7714946cf96 100644 --- a/github/git_trees.go +++ b/github/git_trees.go @@ -44,6 +44,20 @@ func (t TreeEntry) String() string { return Stringify(t) } +// treeEntryWithFileDelete is used internally to delete a file whose +// Content and SHA fields are empty. It does this by removing the "omitempty" +// tag modifier on the SHA field which causes the GitHub API to receive +// {"sha":null} and thereby delete the file. +type treeEntryWithFileDelete struct { + SHA *string `json:"sha"` + Path *string `json:"path,omitempty"` + Mode *string `json:"mode,omitempty"` + Type *string `json:"type,omitempty"` + Size *int `json:"size,omitempty"` + Content *string `json:"content,omitempty"` + URL *string `json:"url,omitempty"` +} + func (t *TreeEntry) MarshalJSON() ([]byte, error) { if t.SHA == nil && t.Content == nil { return json.Marshal(struct { @@ -102,8 +116,8 @@ func (s *GitService) GetTree(ctx context.Context, owner string, repo string, sha // createTree represents the body of a CreateTree request. type createTree struct { - BaseTree string `json:"base_tree,omitempty"` - Entries []TreeEntry `json:"tree"` + BaseTree string `json:"base_tree,omitempty"` + Entries []interface{} `json:"tree"` } // CreateTree creates a new tree in a repository. If both a tree and a nested @@ -114,9 +128,24 @@ type createTree struct { func (s *GitService) CreateTree(ctx context.Context, owner string, repo string, baseTree string, entries []TreeEntry) (*Tree, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo) + newEntries := make([]interface{}, 0, len(entries)) + for _, entry := range entries { + if entry.Content == nil && entry.SHA == nil { + newEntries = append(newEntries, treeEntryWithFileDelete{ + Path: entry.Path, + Mode: entry.Mode, + Type: entry.Type, + Size: entry.Size, + URL: entry.URL, + }) + continue + } + newEntries = append(newEntries, entry) + } + body := &createTree{ BaseTree: baseTree, - Entries: entries, + Entries: newEntries, } req, err := s.client.NewRequest("POST", u, body) if err != nil { diff --git a/github/git_trees_test.go b/github/git_trees_test.go index 1d1b789cd6f..3e76527403c 100644 --- a/github/git_trees_test.go +++ b/github/git_trees_test.go @@ -6,9 +6,10 @@ package github import ( + "bytes" "context" - "encoding/json" "fmt" + "io/ioutil" "net/http" "reflect" "testing" @@ -68,17 +69,16 @@ func TestGitService_CreateTree(t *testing.T) { } mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { - v := new(createTree) - json.NewDecoder(r.Body).Decode(v) + got, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("unable to read body: %v", err) + } testMethod(t, r, "POST") - want := &createTree{ - BaseTree: "b", - Entries: input, - } - if !reflect.DeepEqual(v, want) { - t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) + want := []byte(`{"base_tree":"b","tree":[{"sha":"7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b","path":"file.rb","mode":"100644","type":"blob"}]}` + "\n") + if !bytes.Equal(got, want) { + t.Errorf("Git.CreateTree request body: %s, want %s", got, want) } fmt.Fprint(w, `{ @@ -132,17 +132,16 @@ func TestGitService_CreateTree_Content(t *testing.T) { } mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { - v := new(createTree) - json.NewDecoder(r.Body).Decode(v) + got, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("unable to read body: %v", err) + } testMethod(t, r, "POST") - want := &createTree{ - BaseTree: "b", - Entries: input, - } - if !reflect.DeepEqual(v, want) { - t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) + want := []byte(`{"base_tree":"b","tree":[{"path":"content.md","mode":"100644","content":"file content"}]}` + "\n") + if !bytes.Equal(got, want) { + t.Errorf("Git.CreateTree request body: %s, want %s", got, want) } fmt.Fprint(w, `{ @@ -198,17 +197,16 @@ func TestGitService_CreateTree_Delete(t *testing.T) { } mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { - v := new(createTree) - json.NewDecoder(r.Body).Decode(v) + got, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("unable to read body: %v", err) + } testMethod(t, r, "POST") - want := &createTree{ - BaseTree: "b", - Entries: input, - } - if !reflect.DeepEqual(v, want) { - t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) + want := []byte(`{"base_tree":"b","tree":[{"sha":null,"path":"content.md","mode":"100644"}]}` + "\n") + if !bytes.Equal(got, want) { + t.Errorf("Git.CreateTree request body: %s, want %s", got, want) } fmt.Fprint(w, `{