Sembra che tu abbia alcuni problemi qui:
1. Identificazione di funzionalità per una versione specifica
Questo è un problema di gestione del progetto e un problema di coordinamento. La funzione questa sarà rilasciata prima, nello stesso momento o dopo questa altra funzione? Se i rilasci vogliono accadere una funzione alla volta, allora identificali. Se le caratteristiche verranno raggruppate in versioni, quindi capire quali sono i raggruppamenti e applicarli con i responsabili e ai responsabili delle decisioni. Usa il tuo sistema di tracciamento o emissione dei biglietti per taggare i comunicati. Metti in chiaro che se una caratteristica di una specifica release è un no-go, allora sono tutte.
2. Strategie di ramificazione
Git-flow è la risposta facile per problemi come questi, e spesso le persone usano una variante di git-flow anche se non sanno cosa sia. Non dirò che è un trucco per tutti i problemi, ma aiuta molto.
Sembra che tu stia imbattendo in un problema con strategie di rilascio non deterministiche, in cui le funzionalità sono approvate scattershot e qualcosa che ha iniziato lo sviluppo molto tempo fa potrebbe essere rilasciato dopo qualcosa che è stato avviato più di recente: le funzionalità di leap-frog.
Filiali caratteristiche a lungo termine o rami di rilascio simultanei sono probabilmente la risposta migliore per questo tipo di problemi. Unisci (o rebase, se ti senti a tuo agio con esso) l'ultimo dal master nei tuoi rami di lunga data. Fai attenzione a solo unire in funzioni già esistenti, altrimenti ti imbatterai nei problemi che hai avuto ora (troppe funzioni miste su un ramo).
I rami "Hotfix" o "bugfix" sono una parte essenziale di questo processo; usali per piccole correzioni una tantum con un ciclo di QA breve.
Dalla tua descrizione, potrebbe anche essere meglio non mantenere un ramo "di sviluppo" ufficiale. Piuttosto, separa tutte le funzioni dal master e crea rami di rilascio uniti una volta identificato il rilascio.
3. Ambienti
Non associare i rami git ai tuoi ambienti, ad eccezione della produzione == master. Il ramo "sviluppo" dovrebbe essere considerato rotto. I rami di rilascio vengono spinti per testare gli ambienti, sia che si tratti di un ambiente di controllo qualità o di un sistema di gestione temporanea. Se è necessario, inserire un ramo di funzione specifico in un ambiente.
Se hai più di un ramo di funzionalità che devono essere rilasciati separatamente ma vengono testati contemporaneamente ... ¯ \ _ (ツ) _ / ¯ .... far girare un altro server? Forse unirli insieme in un ramo di riserva ... commetti correzioni / modifiche ai rami originali e ri-unisci nel ramo di lancio; fai l'approvazione finale e la UAT sui singoli rami di rilascio.
4. Rimozione di funzionalità non approvate da un ramo
Questo è ciò che i pensieri di cui sopra stanno cercando di evitare, perché questa è senza dubbio la cosa più dolorosa da provare e fare. Se sei fortunato, le funzionalità sono state unite nel tuo sviluppo o testare filiali atomicamente usando commit di unione. Se sei sfortunato, gli sviluppatori si sono impegnati direttamente nel ramo sviluppo / test.
In ogni caso, se ti stai preparando per una versione e hai modifiche non approvate, dovrai utilizzare Git per indietro quei commit non approvati dal ramo di rilascio; l'idea migliore è quella di prima testare la versione.
Buona fortuna.