Uni-Dimensional String Golf
Python 2 - 380 bytes

Try it online

Shorter answer, thanks to @ovs. This one requires list of integers and string as input. Example: 28,26,"ABCDEFGHIJKLM()NOPQRSTUVWXYZ". 
Check edits for older version, that accepts plain string like "28,26,ABCDEFGHIJKLM()NOPQRSTUVWXYZ"

I=input()
c=-1
G=I[c]
I=map(int,I[:c])
u=r=""
L=[u]*len(G)
H=len(G.split('(')[0])
L[H:H+1]='()'
d=1
s=0
f="".join
for h in I:
 s+=1;c+=h*d;d=(1,-1)[H-c&lt;0];T=f(L)
 if H&lt;=c&lt;H+2:u='o'if s==len(I)else'X';break
 if c&lt;0:r="^"+T;break
 if c&gt;len(G):r=T+"$";break
 L[c]=G[c]
else:
 m=H-c if~d else c-H-1
 if d&gt;0:c+=1
 L=L[:c]+[str(m)]+L[c:];r=f(L)
print r or("%s)"%u).join(f(L).split(')'))


Explanation (for older version, but approach is same):

I=input().split(',')             # split input
G=I[-1]                          # last element - Green
I=map(int,I[:-1])                # convert all other (hits) to int
L=[""]*len(G)                    # empty list of same length as Green, used to ouput
H=len(G.split('(')[0])           # index of hole
L[H]='('                         # hole is always printed
L[H+1]=')'
c=-1                             # current index on Green
d=1                              # direction of hit
u=r=""                           # strings for output and in-hole marker (X)/(o)
s=0                              # number of current hit
for h in I:
    s+=1
    c+=h*d                       # move current by hit * direction
    d=(1,-1)[H-c&lt;0]              # calculate new direction
    T="".join(L)                 # used to remove code duplication 
    if H&lt;=c&lt;H+2:                 # if in hole
        u='o'if s==len(I)else'X' # check whether it is last hit or not
        break
    if c&lt;0:                      # if out of Green on the left
        r="^"+T                  # add '^' and join all visited indices
        break
    if c&gt;len(G):                 # if out of Green on the right
        r=T+"$"                  # add '&amp;' after join all visited indices
        break
    L[c]=G[c]                    # mark visited index
else:                            # rare occasion of for-else construction - executes if never break out of loop
                                 # which means, that we are out of hits and not in hole
    m=H-c if~d else c-H-1        # calculate distance to hole
    if d&gt;0:
        c+=1
    L=L[:c]+[str(m)]+L[c:]       # expand L list with distance
    r="".join(L)                 # join list
t="".join(L).split(')')          
if r:
    print r                      # ouput r in all cases exept in-hole
else:
    print t[0]+"%s)"%u+t[1]      # output for in-hole cases