Imported Upstream version 2.5.1
[scm/test.git] / git / refs.go
1 package git
2
3 import (
4         "fmt"
5
6         "github.com/rubyist/tracerx"
7 )
8
9 type RefUpdate struct {
10         git    Env
11         remote string
12         left   *Ref
13         right  *Ref
14 }
15
16 func NewRefUpdate(g Env, remote string, l, r *Ref) *RefUpdate {
17         return &RefUpdate{
18                 git:    g,
19                 remote: remote,
20                 left:   l,
21                 right:  r,
22         }
23 }
24
25 func (u *RefUpdate) Left() *Ref {
26         return u.left
27 }
28
29 func (u *RefUpdate) LeftCommitish() string {
30         return refCommitish(u.Left())
31 }
32
33 func (u *RefUpdate) Right() *Ref {
34         if u.right == nil {
35                 u.right = defaultRemoteRef(u.git, u.remote, u.Left())
36         }
37         return u.right
38 }
39
40 // defaultRemoteRef returns the remote ref receiving a push based on the current
41 // repository config and local ref being pushed.
42 //
43 // See push.default rules in https://git-scm.com/docs/git-config
44 func defaultRemoteRef(g Env, remote string, left *Ref) *Ref {
45         pushMode, _ := g.Get("push.default")
46         switch pushMode {
47         case "", "simple":
48                 brRemote, _ := g.Get(fmt.Sprintf("branch.%s.remote", left.Name))
49                 if brRemote == remote {
50                         // in centralized workflow, work like 'upstream' with an added safety to
51                         // refuse to push if the upstream branch’s name is different from the
52                         // local one.
53                         return trackingRef(g, left)
54                 }
55
56                 // When pushing to a remote that is different from the remote you normally
57                 // pull from, work as current.
58                 return left
59         case "upstream", "tracking":
60                 // push the current branch back to the branch whose changes are usually
61                 // integrated into the current branch
62                 return trackingRef(g, left)
63         case "current":
64                 // push the current branch to update a branch with the same name on the
65                 // receiving end.
66                 return left
67         default:
68                 tracerx.Printf("WARNING: %q push mode not supported", pushMode)
69                 return left
70         }
71 }
72
73 func trackingRef(g Env, left *Ref) *Ref {
74         if merge, ok := g.Get(fmt.Sprintf("branch.%s.merge", left.Name)); ok {
75                 return ParseRef(merge, "")
76         }
77         return left
78 }
79
80 func (u *RefUpdate) RightCommitish() string {
81         return refCommitish(u.Right())
82 }
83
84 func refCommitish(r *Ref) string {
85         if len(r.Sha) > 0 {
86                 return r.Sha
87         }
88         return r.Name
89 }
90
91 // copy of env
92 type Env interface {
93         Get(key string) (val string, ok bool)
94 }