Hoe kan ik meerdere git-commits terugdraaien?

Ik heb een git-repository die er als volgt uitziet:

A <- B <- C <- D <- HEAD

Ik wil dat het hoofd van de tak naar A wijst, d.w.z. ik wil dat B, C, D en HEAD verdwijnen en ik wil dat hoofd synoniem is met A.

Het klinkt alsof ik kan proberen om te rebasen (niet van toepassing, aangezien ik tussendoor wijzigingen heb doorgevoerd), of terug kan keren. Maar hoe zet ik meerdere commits terug? Moet ik één voor één terugkeren? Is de bestelling belangrijk?


Antwoord 1, autoriteit 100%

Uitbreiden wat ik in een opmerking heb geschreven

De algemene regel is dat je de geschiedenis die je hebt gepubliceerd niet moet herschrijven (wijzigen), omdat iemand zijn werk daarop kan hebben gebaseerd. Als je de (wijzigings)geschiedenis herschrijft, zou je problemen krijgen met het samenvoegen van hun wijzigingen en met het bijwerken ervan.

De oplossing is dus om een nieuwe committe maken die wijzigingen terugdraaitdie je wilt verwijderen. U kunt dit doen met git revertcommando.

U heeft de volgende situatie:

A <-- B <-- C <-- D <-- master <-- HOOFD

(pijlen verwijzen hier naar de richting van de aanwijzer: de “ouder”-referentie in het geval van commits, de top-commit in het geval van branch head (branch ref), en de naam van branch in het geval van HEAD-referentie ).

Wat u moet maken, is het volgende:

A <-- B <-- C <-- D <-- [(BCD)-1] <-- master <-- HEAD

Indien [(BCD)^-1]: de commit die veranderingen in Commits B, C, D. Wiskunde vertelt, vertelt ons dat (BCD) -1 = D -1 C -1 B -1 , zodat u de vereiste situatie kunt krijgen met behulp van de volgende opdrachten:

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message for all of them"

Werkt voor alles behalve samenvoeging.


Alternatieve oplossing zou zijn voor Afrekenen Inhoud van commit a en verbinden deze toestand. Werkt ook met samenvoeging. Toegevoegd bestanden worden echter niet verwijderd. Als u lokale wijzigingen hebt git stashITS TERWOORD:

$ git checkout -f A -- . # checkout that revision over the top of local files
$ git commit -a

Dan zou u de volgende situatie hebben:

A & LT; - B & LT; - C & LT; - D & LT; - A '& LT; - Master & LT; - Hoofd

De commit a ‘heeft dezelfde inhoud als commit a, maar is een andere commit (commit-boodschap, ouders, commit datum).


Alternate Oplossing door Jeff Ferland, gewijzigd door Charles Bailey bouwt voort op de hetzelfde idee, maar gebruikt Git Reset . Hier is het enigszins gewijzigd, op deze manier werkt voor alles:

$ git reset --hard A
$ git reset --soft D # (or ORIG_HEAD or @{1} [previous location of HEAD]), all of which are D
$ git commit

Antwoord 2, Autoriteit 26%

Schone manier die ik nuttig vond

git revert --no-commit HEAD~3..

Deze opdracht-reverts laatste 3 commits met slechts één commit.

Herschrijft ook geen geschiedenis.

De ..Helpt bij het maken van een bereik. Betekenis HEAD~3..is hetzelfde als HEAD~3..HEAD


Antwoord 3, Autoriteit 16%

Om dit te doen, moet u gewoon de -opdracht gebruiken, met het opgeven van het bereik van commits die u wilt retreerd.

Rekening houdend met uw voorbeeld, zou u dit moeten doen (ervan uitgaande dat u op de tak ‘Master’) bent):

git revert master~3..master

of git revert B...Dof git revert D C B

Hiermee wordt een nieuwe commit voorkomt in uw lokale met de inverse commit van B, C en D (wat betekent dat het veranderingen door deze commits is ingevoerd):

A <- B <- C <- D <- BCD' <- HEAD

Antwoord 4, Autoriteit 5%

Zoals het antwoord van Jakub, kunt u eenvoudig opeenvolgende commits selecteren om terug te keren.

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'

Antwoord 5, Autoriteit 5%

git reset --hard a
git reset --mixed d
git commit

Dat zal als een keer optreden als een keer tegelijkertijd. Geef een goed commit-bericht.


Antwoord 6, Autoriteit 4%

Zorg er eerst voor dat uw werkkopie niet is gewijzigd. Dan:

git diff --binary HEAD commit_sha_you_want_to_revert_to | git apply

En dan gewoon committeren. Vergeet niet om te documenteren wat de reden is om terug te keren.


Antwoord 7, Autoriteit 3%

Ik ben zo gefrustreerd dat deze vraag niet zomaar kan worden beantwoord. Elke andere vraag heeft betrekking op hoe correct terug te keren en de geschiedenis te bewaren. Deze vraag zegt “Ik wil dat het hoofd van de vertakking naar A wijst, dwz ik wil dat B, C, D en HEAD verdwijnenen ik wil dat hoofd synoniem is met A.”

git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f

Ik heb veel geleerd door het bericht van Jakub te lezen, maar een man in het bedrijf (met toegang tot onze “test”-branch zonder Pull-Request) pushte als 5 slechte commits om een fout te herstellen en te herstellen die hij maakte 5 geleden begaat. Niet alleen dat, maar een of twee Pull Requests werden geaccepteerd, die nu slecht waren. Dus vergeet het maar, ik vond de laatste goede commit (abc1234) en draaide gewoon het basisscript:

git checkout testing
git reset --hard abc1234
git push -f

Ik heb de andere 5 jongens die in deze repo werken verteld dat ze beter hun wijzigingen van de afgelopen paar uur kunnen noteren en de laatste tests kunnen wissen/opnieuw vertakken. Einde van het verhaal.


Antwoord 8

Dit is een uitbreiding van een van de oplossingen in het antwoord van Jakub

Ik werd geconfronteerd met een situatie waarin de commits die ik moest terugdraaien nogal complex waren, waarbij verschillende van de commits merge commits waren, en ik moest voorkomen dat ik de geschiedenis herschreef. Ik was niet in staat om een reeks git revert-commando’s te gebruiken omdat ik uiteindelijk conflicten tegenkwam tussen de reversiewijzigingen die werden toegevoegd. Ik heb uiteindelijk de volgende stappen gebruikt.

Bekijk eerst de inhoud van de doel-commit terwijl je HEAD aan het uiteinde van de branch laat:

$ git checkout -f <target-commit> -- .

(de – zorgt ervoor dat <target-commit>wordt geïnterpreteerd als een commit in plaats van een bestand; de. verwijst naar de huidige map.)

Bepaal dan welke bestanden zijn toegevoegd in de commits die worden teruggedraaid en moeten dus worden verwijderd:

$ git diff --name-status --cached <target-commit>

Bestanden die zijn toegevoegd, moeten worden weergegeven met een “A” aan het begin van de lijn, en er zouden geen andere verschillen moeten zijn. Als er geen bestanden moeten worden verwijderd, voert u deze bestanden op voor verwijdering:

$ git rm <filespec>[ <filespec> ...]

Begin ten slotte de terugkeer:

$ git commit -m 'revert to <target-commit>'

Zorg indien gewenst dat we terug zijn op de gewenste staat:

$git diff <target-commit> <current-commit>

Er zouden geen verschillen moeten zijn.


Antwoord 9

De gemakkelijke manier om een ​​groep commits terug te keren op gedeelde repository (dat mensen gebruiken en u wilt de geschiedenis behouden) is om te gebruiken git revertin combinatie met GIT rev-list. De laatste zal u een lijst met commits leveren, de eerste zal de terugkeer zelf doen.

Er zijn twee manieren om dat te doen. Als u wilt dat de meerdere commits in één voorleggingsgebruik terugvindt:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-commit $i; done

Hierdoor wordt een groep commits teruggedraaid die u nodig hebt, maar laat alle wijzigingen in uw werkboom, u moet ze daarna net zo gebruik maken.

Een andere optie is om een ​​enkele commit te hebben per teruggekeerde verandering:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

Als u bijvoorbeeld een plattegrond zoals

hebt

o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

Om de wijzigingen van EEE terug te keren naar BBB , voert u

uit

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done

Antwoord 10

Niets van die werkte voor mij, dus ik had drie commits om te herstellen (de laatste drie commits), dus ik deed:

git revert HEAD
git revert HEAD~2
git revert HEAD~4
git rebase -i HEAD~3 # pick, squash, squash

werkte als een charme 🙂


Antwoord 11

Naar mijn mening kan een zeer eenvoudige en schone manier zijn:

Ga terug naar een

git checkout -f A

Point Master’s Head naar de huidige status

git symbolic-ref HEAD refs/heads/master

Opslaan

git commit

Antwoord 12

Waarschijnlijk minder elegant dan andere benaderingen hier, maar ik heb altijd gebruikt get reset --hard HEAD~Nom meerdere commits ongedaan te maken, waar Nhet aantal is Commits die je wilt teruggaan.

of, indien niet zeker van het exacte aantal commits, gewoon uitgevoerd git reset --hard HEAD^(die één commit) meerdere keren teruggaat totdat u de gewenste toestand bereikt.


Antwoord 13

Ik wilde echt hard worden vermijden, dit is waar ik mee kwam.

A -> B -> C -> D -> HEAD

Om terug te gaan naar een (die 4 stappen terug is):

git pull                  # Get latest changes
git reset --soft HEAD~4   # Set back 4 steps
git stash                 # Stash the reset
git pull                  # Go back to head
git stash pop             # Pop the reset 
git commit -m "Revert"    # Commit the changes

Antwoord 14

Als u

  1. hebben een samengevoegde commit en
  2. U kunt niet terugkeren en
  3. je vindt het niet erg om de geschiedenis die je moet terugdraaien te vernietigen,

dan kun je

git reset --soft HEAD~(number of commits you'd like to revert)
git commit -m "The stuff you didn't like."
git log
# copy the hash of your last commit
git revert <hash of your last (squashed) commit>

Als u uw wijzigingen wilt doorvoeren, vergeet dan niet de vlag -fte gebruiken omdat u de geschiedenis heeft gewijzigd

git push <your fork> <your branch> -f

Antwoord 15

Ik merkte dat ik een groot aantal commits moest terugdraaien en ze vervolgens opnieuw moest terugzetten om een team te helpen een duidelijk pull-verzoek te presenteren zonder dat ze hun doel-branch (die direct was vastgelegd) moesten forceren.

# checkout the branch that should be targeted
git checkout $branch_target
# revert the commits in $branch_target to some $count where
#   $count is the number of commits to revert
#   cut is used to slice just the commit hash field from each line of output
#   xargs runs the command once for each line of input, reversing the commits!
git log --oneline -n $count | cut -d' ' -f1 | xargs git revert
# check out the branch which should be the source of the pull request
git checkout -b $branch_for_pull
# revert the revert commits
# $count is that same number of commits being reverted (again)
git log --oneline -n $count | cut -d' ' -f1 | xargs git revert
# push branches up and go off to create PR in whatever web UI
git push --set-upstream origin $branch_for_pull  # it's new!
git checkout $branch_target
git push  # if this branch wasn't pushed, just fix the issue locally instead..

Omdat dit alle commits terugzet van HEADnaar git log -n $countin omgekeerde volgorde, zal het goed en netjes werken met een willekeurig aantal commits

Bekijken vanaf $branch_targetin deze staat

% git log --oneline origin/$branch_target
ffff006 (origin/$branch_target, $branch_target) Revert "first commit"
ffff005 Revert "second commit"
ffff004 Revert "third commit"
ffff003 third commit
ffff002 second commit
ffff001 first commit

Bekijken vanaf $branch_for_pullin deze staat

% git log --oneline origin/$branch_for_pull
ffff009 (origin/$branch_for_pull, $branch_for_pull) Revert "Revert "third commit""
ffff008 Revert "Revert "second commit""
ffff007 Revert "Revert "first commit""
ffff006 (origin/$branch_target, $branch_target) Revert "first commit"
ffff005 Revert "second commit"
ffff004 Revert "third commit"
ffff003 third commit
ffff002 second commit
ffff001 first commit

Als het de bedoeling was om N branches met wijzigingensets te maken, maar ze waren allemaal gecommitteerd aan dezelfde branch, kun je ze nog steeds allemaal terugzetten naar de basiscommit, en dan alleen de benodigde reverts terugzetten, aangezien de wijzigingensets logisch geordend moeten worden (probeer dat 5x snel te zeggen)

Het gebruik van een syntaxis zoals HEAD~7..HEAD~5kan helpen bij het beschrijven van de bereiken om de revert-revert takken nauwkeurig te splitsen

Hier zou het logisch zijn bij het terugzetten van de laatste 7 commits (git log -n 7), maar bij het herstellen van 5 met in één branch (git log -n 5) en 2 dan de bovenste 2 in een andere git log HEAD~12..HEAD~10(12 is 7 commits + 5 commits, ervan uitgaande dat de nieuwe PR-tak is gebaseerd op ofwel de tak “voor ” it, of het resultaat van een FF (niet-geplet) merge de branch “voor” het in de originele doelbranch)


Antwoord 16

Als je de commits van een feature tijdelijk wilt terugdraaien, dan kun je de reeks volgende commando’s gebruiken.

Zo werkt het

git log –pretty=oneline | grep ‘feature_name’ | knippen -d ‘ ‘ -f1 | xargs -n1 git revert –no-edit

Other episodes