@@ -21,10 +21,19 @@ type State struct {
2121
2222 Client Client
2323
24- lineage string
25- serial uint64
26- state , readState * states.State
27- disableLocks bool
24+ // We track two pieces of meta data in addition to the state itself:
25+ //
26+ // lineage - the state's unique ID
27+ // serial - the monotonic counter of "versions" of the state
28+ //
29+ // Both of these (along with state) have a sister field
30+ // that represents the values read in from an existing source.
31+ // All three of these values are used to determine if the new
32+ // state has changed from an existing state we read in.
33+ lineage , readLineage string
34+ serial , readSerial uint64
35+ state , readState * states.State
36+ disableLocks bool
2837}
2938
3039var _ statemgr.Full = (* State )(nil )
@@ -64,8 +73,15 @@ func (s *State) WriteStateForMigration(f *statefile.File, force bool) error {
6473 s .mu .Lock ()
6574 defer s .mu .Unlock ()
6675
67- checkFile := statefile .New (s .state , s .lineage , s .serial )
68- if ! force {
76+ // `force` is passed down from the CLI flag and terminates here. Actual
77+ // force pushing with the remote backend happens when Put()'ing the contents
78+ // in the backend. If force is specified we skip verifications and hand the
79+ // context off to the client to use when persitence operations actually take place.
80+ c , isForcePusher := s .Client .(ClientForcePusher )
81+ if force && isForcePusher {
82+ c .EnableForcePush ()
83+ } else {
84+ checkFile := statefile .New (s .state , s .lineage , s .serial )
6985 if err := statemgr .CheckValidImport (f , checkFile ); err != nil {
7086 return err
7187 }
@@ -113,7 +129,12 @@ func (s *State) refreshState() error {
113129 s .lineage = stateFile .Lineage
114130 s .serial = stateFile .Serial
115131 s .state = stateFile .State
116- s .readState = s .state .DeepCopy () // our states must be separate instances so we can track changes
132+
133+ // Properties from the remote must be separate so we can
134+ // track changes as lineage, serial and/or state are mutated
135+ s .readLineage = stateFile .Lineage
136+ s .readSerial = stateFile .Serial
137+ s .readState = s .state .DeepCopy ()
117138 return nil
118139}
119140
@@ -123,8 +144,11 @@ func (s *State) PersistState() error {
123144 defer s .mu .Unlock ()
124145
125146 if s .readState != nil {
126- if statefile .StatesMarshalEqual (s .state , s .readState ) {
127- // If the state hasn't changed at all then we have nothing to do.
147+ lineageUnchanged := s .readLineage != "" && s .lineage == s .readLineage
148+ serialUnchanged := s .readSerial != 0 && s .serial == s .readSerial
149+ stateUnchanged := statefile .StatesMarshalEqual (s .state , s .readState )
150+ if stateUnchanged && lineageUnchanged && serialUnchanged {
151+ // If the state, lineage or serial haven't changed at all then we have nothing to do.
128152 return nil
129153 }
130154 s .serial ++
@@ -161,7 +185,13 @@ func (s *State) PersistState() error {
161185
162186 // After we've successfully persisted, what we just wrote is our new
163187 // reference state until someone calls RefreshState again.
188+ // We've potentially overwritten (via force) the state, lineage
189+ // and / or serial (and serial was incremented) so we copy over all
190+ // three fields so everything matches the new state and a subsequent
191+ // operation would correctly detect no changes to the lineage, serial or state.
164192 s .readState = s .state .DeepCopy ()
193+ s .readLineage = s .lineage
194+ s .readSerial = s .serial
165195 return nil
166196}
167197
0 commit comments