@@ -3,7 +3,9 @@ package cli_test
33import (
44 "context"
55 "os"
6+ "os/exec"
67 "path/filepath"
8+ "strings"
79 "testing"
810
911 "github.com/tmuxpack/tpack/internal/git"
@@ -103,3 +105,87 @@ func TestPuller_PullWithBranch(t *testing.T) {
103105 t .Fatalf ("expected feature.txt after pull with branch: %v" , err )
104106 }
105107}
108+
109+ // revParse returns the resolved SHA of HEAD (or any ref) for a repo.
110+ func revParse (t * testing.T , dir , ref string ) string {
111+ t .Helper ()
112+ out , err := exec .CommandContext (context .Background (),
113+ "git" , "-C" , dir , "rev-parse" , ref ).Output ()
114+ if err != nil {
115+ t .Fatalf ("rev-parse %s in %s: %v" , ref , dir , err )
116+ }
117+ return strings .TrimSpace (string (out ))
118+ }
119+
120+ func TestPuller_PullSkippedOnTag (t * testing.T ) {
121+ if testing .Short () {
122+ t .Skip ("skipping git CLI test in short mode" )
123+ }
124+
125+ bare := initBareRepo (t )
126+
127+ // Tag the initial commit and push the tag upstream.
128+ tagger := cloneLocal (t , bare )
129+ runGit (t , tagger , "tag" , "v1.0.0" )
130+ runGit (t , tagger , "push" , "origin" , "v1.0.0" )
131+ tagSHA := revParse (t , tagger , "v1.0.0" )
132+
133+ // Clone fresh, then check out the tag so HEAD is detached.
134+ clone := cloneLocal (t , bare )
135+ runGit (t , clone , "fetch" , "--tags" )
136+ runGit (t , clone , "checkout" , "v1.0.0" )
137+
138+ // Push a new commit upstream so a naive `git pull` would fast-forward.
139+ addCommitToBare (t , bare , "after-tag.txt" )
140+
141+ puller := gitcli .NewPuller ()
142+ _ , err := puller .Pull (context .Background (), git.PullOptions {
143+ Dir : clone ,
144+ Branch : "v1.0.0" ,
145+ })
146+ if err != nil {
147+ t .Fatalf ("Pull pinned to tag returned error: %v" , err )
148+ }
149+
150+ if got := revParse (t , clone , "HEAD" ); got != tagSHA {
151+ t .Fatalf ("HEAD moved off tag: HEAD=%s, want %s" , got , tagSHA )
152+ }
153+
154+ // The new file must NOT be present; pull should have been skipped.
155+ if _ , err := os .Stat (filepath .Join (clone , "after-tag.txt" )); err == nil {
156+ t .Fatal ("after-tag.txt should not exist; pull should have been skipped on detached HEAD" )
157+ }
158+ }
159+
160+ func TestPuller_PullSkippedOnCommitSHA (t * testing.T ) {
161+ if testing .Short () {
162+ t .Skip ("skipping git CLI test in short mode" )
163+ }
164+
165+ bare := initBareRepo (t )
166+
167+ // Capture the initial commit SHA and check it out (detached HEAD).
168+ clone := cloneLocal (t , bare )
169+ sha := revParse (t , clone , "HEAD" )
170+ runGit (t , clone , "checkout" , sha )
171+
172+ // Push a new commit upstream so a naive `git pull` would fast-forward.
173+ addCommitToBare (t , bare , "after-sha.txt" )
174+
175+ puller := gitcli .NewPuller ()
176+ _ , err := puller .Pull (context .Background (), git.PullOptions {
177+ Dir : clone ,
178+ Branch : sha ,
179+ })
180+ if err != nil {
181+ t .Fatalf ("Pull pinned to SHA returned error: %v" , err )
182+ }
183+
184+ if got := revParse (t , clone , "HEAD" ); got != sha {
185+ t .Fatalf ("HEAD moved off pinned SHA: HEAD=%s, want %s" , got , sha )
186+ }
187+
188+ if _ , err := os .Stat (filepath .Join (clone , "after-sha.txt" )); err == nil {
189+ t .Fatal ("after-sha.txt should not exist; pull should have been skipped on detached HEAD" )
190+ }
191+ }
0 commit comments