/*
Get information about the merge request including its files and changes.

    GET /projects/:id/merge_request/:merge_request_id/changes

Parameters:

    id               The ID of a project
    merge_request_id The ID of a merge request

*/
357947
func (g *Gitlab) ProjectMergeRequestChanges(id, merge_request_id string) (*MergeRequestChanges, *ResponseMeta, error) {
	u := g.ResourceUrl(ProjectMergeRequestChangesApiPath, map[string]string{
		":id":               id,
		":merge_request_id": merge_request_id,
	})

	var err error
	changes := new(MergeRequestChanges)

	contents, meta, err := g.buildAndExecRequest("GET", u.String(), nil)
	if err == nil {
		err = json.Unmarshal(contents, &changes)
	}

	return changes, meta, err
}
351399
func OverlayChanges(layers []string, rw string) ([]Change, error) {
	dc := func(root, path string, fi os.FileInfo) (string, error) {
		return overlayDeletedFile(layers, root, path, fi)
	}
	return changes(layers, rw, dc, nil, overlayLowerContainsWhiteout)
}
346977
func (w *relationStatusWatcher) mergeChanges(current, new []watcher.RelationStatusChange) []watcher.RelationStatusChange {
	chMap := make(map[string]watcher.RelationStatusChange)
	for _, c := range current {
		chMap[c.Key] = c
	}
	for _, c := range new {
		chMap[c.Key] = c
	}
	var result []watcher.RelationStatusChange
	for _, c := range chMap {
		result = append(result, c)
	}
	return result
}
1079365
func (s *ChangesService) SubmitChange(changeID string, input *SubmitInput) (*ChangeInfo, *Response, error) {
	return s.change("submit", changeID, input)
}
273127
func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
	return d.naiveDiff.Changes(id, parent)
}
81493
func (w *offerStatusWatcher) mergeChanges(current, new []watcher.OfferStatusChange) []watcher.OfferStatusChange {
	chMap := make(map[string]watcher.OfferStatusChange)
	for _, c := range current {
		chMap[c.Name] = c
	}
	for _, c := range new {
		chMap[c.Name] = c
	}
	var result []watcher.OfferStatusChange
	for _, c := range chMap {
		result = append(result, c)
	}
	return result
}
1173585
func (cfg *Config) merge(other Config) {
	if dir := other.StoragePath; len(dir) > 0 {
		cfg.StoragePath = dir
	}

	if pin := other.Pin; len(pin) > 0 {
		cfg.Pin = pin
	}

	if port := other.Port; len(port) > 0 {
		cfg.Port = ":" + port
	}

	if ip := other.IP; len(ip) > 0 {
		cfg.IP = ip
	}
}
417314
func (s *RouterService) NewChangeServiceForRouterParams(id string, serviceofferingid string) *ChangeServiceForRouterParams {
	p := &ChangeServiceForRouterParams{}
	p.p = make(map[string]interface{})
	p.p["id"] = id
	p.p["serviceofferingid"] = serviceofferingid
	return p
}
1237205
func (s *ChangesService) change(tail string, changeID string, input interface{}) (*ChangeInfo, *Response, error) {
	u := fmt.Sprintf("changes/%s/%s", changeID, tail)
	req, err := s.client.NewRequest("POST", u, input)
	if err != nil {
		return nil, nil, err
	}

	v := new(ChangeInfo)
	resp, err := s.client.Do(req, v)
	if err != nil {
		return nil, resp, err
	}
	if resp.StatusCode == http.StatusConflict {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return v, resp, err
		}
		return v, resp, errors.New(string(body[:]))
	}
	return v, resp, nil
}
1116871
func (d *DeploysService) Get(id string) (*Deploy, *Response, error) {
	deploy := &Deploy{Id: id, client: d.client}
	resp, err := deploy.Reload()

	return deploy, resp, err
}
50995
func (p *PullRequest) GetMerged() bool {
	if p == nil || p.Merged == nil {
		return false
	}
	return *p.Merged
}
731805
func (iid *ImageID) Merge(new *ImageID) error {
	if new.Host != "" {
		iid.Host = new.Host
	}
	if new.Port != 0 {
		iid.Port = new.Port
	}
	if new.User != "" {
		iid.User = new.User
	}
	if new.Repo != "" {
		iid.Repo = new.Repo
	}
	if new.Tag != "" {
		iid.Tag = new.Tag
	}
	return nil
}
652144
func (d *NetworkDevice) merge(dev NetworkDevice) bool {
	changed := false
	if d.Port != dev.Port {
		changed = true
		d.Port = dev.Port
	}
	if d.Address != dev.Address {
		changed = true
		d.Address = dev.Address
	}
	if d.Info != dev.Info {
		changed = true
		d.Info = dev.Info
	}
	if d.Name != dev.Name {
		changed = true
		d.Name = dev.Name
	}

	return changed
}
1194745
func (u *UintValue) Merge(onto *uint) {
	if u.v != nil {
		*onto = *(u.v)
	}
}
136483
func (v *Values) Merge(key Key, other *Values) bool {
	v.lock.Lock()
	defer v.lock.Unlock()

	changed := false
	other.EachKeyValue(func(otherKey Key, value interface{}) {
		actualKey := key.Append(otherKey)
		changed = v.put(actualKey, value) || changed
	})
	return changed
}
673251
func (g *Git) Merge(arg string) *command.Model {
	return g.command("merge", arg)
}
274675
func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle MergeStyle, commitDescription string) (err error) {
	defer func() {
		go HookQueue.Add(pr.BaseRepo.ID)
		go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
	}()

	sess := x.NewSession()
	defer sess.Close()
	if err = sess.Begin(); err != nil {
		return err
	}

	if err = pr.Issue.changeStatus(sess, doer, pr.Issue.Repo, true); err != nil {
		return fmt.Errorf("Issue.changeStatus: %v", err)
	}

	headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
	headGitRepo, err := git.OpenRepository(headRepoPath)
	if err != nil {
		return fmt.Errorf("OpenRepository: %v", err)
	}

	// Create temporary directory to store temporary copy of the base repository,
	// and clean it up when operation finished regardless of succeed or not.
	tmpBasePath := path.Join(setting.AppDataPath, "tmp/repos", com.ToStr(time.Now().Nanosecond())+".git")
	os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm)
	defer os.RemoveAll(path.Dir(tmpBasePath))

	// Clone the base repository to the defined temporary directory,
	// and checks out to base branch directly.
	var stderr string
	if _, stderr, err = process.ExecTimeout(5*time.Minute,
		fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath),
		"git", "clone", "-b", pr.BaseBranch, baseGitRepo.Path, tmpBasePath); err != nil {
		return fmt.Errorf("git clone: %s", stderr)
	}

	// Add remote which points to the head repository.
	if _, stderr, err = process.ExecDir(-1, tmpBasePath,
		fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath),
		"git", "remote", "add", "head_repo", headRepoPath); err != nil {
		return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
	}

	// Fetch information from head repository to the temporary copy.
	if _, stderr, err = process.ExecDir(-1, tmpBasePath,
		fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath),
		"git", "fetch", "head_repo"); err != nil {
		return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
	}

	remoteHeadBranch := "head_repo/" + pr.HeadBranch

	// Check if merge style is allowed, reset to default style if not
	if mergeStyle == MERGE_STYLE_REBASE && !pr.BaseRepo.PullsAllowRebase {
		mergeStyle = MERGE_STYLE_REGULAR
	}

	switch mergeStyle {
	case MERGE_STYLE_REGULAR: // Create merge commit

		// Merge changes from head branch.
		if _, stderr, err = process.ExecDir(-1, tmpBasePath,
			fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
			"git", "merge", "--no-ff", "--no-commit", remoteHeadBranch); err != nil {
			return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
		}

		// Create a merge commit for the base branch.
		sig := doer.NewGitSig()
		if _, stderr, err = process.ExecDir(-1, tmpBasePath,
			fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
			"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
			"-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch),
			"-m", commitDescription); err != nil {
			return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, stderr)
		}

	case MERGE_STYLE_REBASE: // Rebase before merging

		// Rebase head branch based on base branch, this creates a non-branch commit state.
		if _, stderr, err = process.ExecDir(-1, tmpBasePath,
			fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
			"git", "rebase", "--quiet", pr.BaseBranch, remoteHeadBranch); err != nil {
			return fmt.Errorf("git rebase [%s on %s]: %s", remoteHeadBranch, pr.BaseBranch, stderr)
		}

		// Name non-branch commit state to a new temporary branch in order to save changes.
		tmpBranch := com.ToStr(time.Now().UnixNano(), 10)
		if _, stderr, err = process.ExecDir(-1, tmpBasePath,
			fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
			"git", "checkout", "-b", tmpBranch); err != nil {
			return fmt.Errorf("git checkout '%s': %s", tmpBranch, stderr)
		}

		// Check out the base branch to be operated on.
		if _, stderr, err = process.ExecDir(-1, tmpBasePath,
			fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
			"git", "checkout", pr.BaseBranch); err != nil {
			return fmt.Errorf("git checkout '%s': %s", pr.BaseBranch, stderr)
		}

		// Merge changes from temporary branch to the base branch.
		if _, stderr, err = process.ExecDir(-1, tmpBasePath,
			fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
			"git", "merge", tmpBranch); err != nil {
			return fmt.Errorf("git merge [%s]: %v - %s", tmpBasePath, err, stderr)
		}

	default:
		return fmt.Errorf("unknown merge style: %s", mergeStyle)
	}

	// Push changes on base branch to upstream.
	if _, stderr, err = process.ExecDir(-1, tmpBasePath,
		fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath),
		"git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil {
		return fmt.Errorf("git push: %s", stderr)
	}

	pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch)
	if err != nil {
		return fmt.Errorf("GetBranchCommit: %v", err)
	}

	pr.HasMerged = true
	pr.Merged = time.Now()
	pr.MergerID = doer.ID
	if _, err = sess.ID(pr.ID).AllCols().Update(pr); err != nil {
		return fmt.Errorf("update pull request: %v", err)
	}

	if err = sess.Commit(); err != nil {
		return fmt.Errorf("Commit: %v", err)
	}

	if err = MergePullRequestAction(doer, pr.Issue.Repo, pr.Issue); err != nil {
		log.Error(2, "MergePullRequestAction [%d]: %v", pr.ID, err)
	}

	// Reload pull request information.
	if err = pr.LoadAttributes(); err != nil {
		log.Error(2, "LoadAttributes: %v", err)
		return nil
	}
	if err = PrepareWebhooks(pr.Issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
		Action:      api.HOOK_ISSUE_CLOSED,
		Index:       pr.Index,
		PullRequest: pr.APIFormat(),
		Repository:  pr.Issue.Repo.APIFormat(nil),
		Sender:      doer.APIFormat(),
	}); err != nil {
		log.Error(2, "PrepareWebhooks: %v", err)
		return nil
	}

	l, err := headGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase)
	if err != nil {
		log.Error(2, "CommitsBetweenIDs: %v", err)
		return nil
	}

	// It is possible that head branch is not fully sync with base branch for merge commits,
	// so we need to get latest head commit and append merge commit manully
	// to avoid strange diff commits produced.
	mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch)
	if err != nil {
		log.Error(2, "GetBranchCommit: %v", err)
		return nil
	}
	if mergeStyle == MERGE_STYLE_REGULAR {
		l.PushFront(mergeCommit)
	}

	commits, err := ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.RepoPath(), pr.BaseRepo.HTMLURL())
	if err != nil {
		log.Error(2, "ToApiPayloadCommits: %v", err)
		return nil
	}

	p := &api.PushPayload{
		Ref:        git.BRANCH_PREFIX + pr.BaseBranch,
		Before:     pr.MergeBase,
		After:      mergeCommit.ID.String(),
		CompareURL: setting.AppURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
		Commits:    commits,
		Repo:       pr.BaseRepo.APIFormat(nil),
		Pusher:     pr.HeadRepo.MustOwner().APIFormat(),
		Sender:     doer.APIFormat(),
	}
	if err = PrepareWebhooks(pr.BaseRepo, HOOK_EVENT_PUSH, p); err != nil {
		log.Error(2, "PrepareWebhooks: %v", err)
		return nil
	}
	return nil
}