Wie entferne ich ausgewählte Commit-Protokolleinträge aus einem Git-Repository, während die Änderungen beibehalten werden?

241

Ich möchte ausgewählte Festschreibungsprotokolleinträge aus einem linearen Festschreibungsbaum entfernen, damit die Einträge nicht im Festschreibungsprotokoll angezeigt werden.

Mein Commit-Baum sieht ungefähr so ​​aus:

R--A--B--C--D--E--HEAD

Ich möchte die Einträge B und C entfernen, damit sie nicht im Festschreibungsprotokoll angezeigt werden, aber Änderungen von A nach D sollten beibehalten werden. Vielleicht durch die Einführung eines einzelnen Commits, sodass B und C zu BC werden und der Baum so aussieht.

R--A--BC--D--E--HEAD

Oder im Idealfall nach A kommt D direkt. D 'repräsentiert Änderungen von A nach B, B nach C und C nach D.

R--A--D'--E--HEAD

Ist das möglich? wenn ja wie?

Dies ist ein ziemlich neues Projekt, daher gibt es derzeit keine Niederlassungen, daher auch keine Zusammenschlüsse.

xk0der
quelle
@ xk0der: "Commits" ist hier der richtige Begriff. rebasekann alte / neue Commits entfernen. Ich weiß nicht, was "Protokolleinträge festschreiben" bedeutet.
JFS
@JFSebastian Ich sehe kein Problem mit "Festschreibungsprotokoll" - Protokoll aller Festschreibungen. Und ich wollte ein paar Einträge aus dem Protokoll löschen - unter Beibehaltung der tatsächlichen Änderungen (der Commits).
xk0der
@ xk0der: Git-Commits sind inhaltsadressierbar, dh wenn Sie etwas an einem Commit ändern , z. B. seine Protokollnachricht; Sie erstellen ein neues Commit. Sie könnten gits Commit ohne Git lesen und sich selbst davon überzeugen .
JFS
@JFSebastian - Danke für die Links - ich weiß das - Aber ändert diese Technik wirklich das Problem, mit dem ich konfrontiert war und wie ich es hervorbrachte? Ich denke nicht. Am Ende: Ich wollte "die Commit-Protokollnachrichten" entfernen - ohne die "Commit-Änderungen" zu entfernen - Bitte lesen Sie meine Frage noch einmal - insbesondere den zweiten Absatz. Um weitere hinzuzufügen git log, wird das "Commit-Protokoll" git-scm.com/docs/git-log angezeigt . Und ich wollte zwei Einträge aus diesem Protokoll entfernen - nicht die Änderungen.
xk0der

Antworten:

273

git-rebase (1) macht genau das.

$ git rebase -i HEAD~5

git awsome-ness [git rebase --interactive] enthält ein Beispiel.

  1. Nicht git-rebasefür öffentliche (Remote-) Commits verwenden.
  2. Stellen Sie sicher, dass Ihr Arbeitsverzeichnis sauber ist ( commitoder stashIhre aktuellen Änderungen).
  3. Führen Sie den obigen Befehl aus. Es startet Ihre $EDITOR.
  4. pickVor Cund Dnach ersetzen squash. Es verschmilzt C und D zu B. Wenn Sie ein Commit löschen möchten, löschen Sie einfach seine Zeile.

Wenn Sie verloren sind, geben Sie Folgendes ein:

$ git rebase --abort  
jfs
quelle
Danke für die schnelle Antwort. Also checke ich A aus und mache eine Rebase, so etwas wie git rebase -i D [A]?
xk0der
3
Wie können wir das auf Remote-Repos machen?
Eray
6
@Eray: nur push -fdeine Änderungen. Tu es nicht, wenn du nicht alleine arbeitest.
JFS
2
@ ripper234: Ich habe Links zu Point git-rebaseManual und Wayback Machine für den Blog-Beitrag behoben .
JFS
75
# detach head and move to D commit
git checkout <SHA1-for-D>

# move HEAD to A, but leave the index and working tree as for D
git reset --soft <SHA1-for-A>

# Redo the D commit re-using the commit message, but now on top of A
git commit -C <SHA1-for-D>

# Re-apply everything from the old D onwards onto this new place 
git rebase --onto HEAD <SHA1-for-D> master
CB Bailey
quelle
Dies funktioniert auch und hat mir geholfen zu verstehen, was ein Soft-Reset ist. Zugegeben, die "Top" -Antwort ist auch richtig und kürzer, aber danke auch für diese Antwort.
CGP
41

Hier ist eine Möglichkeit, eine bestimmte Festschreibungs-ID zu entfernen, wobei nur die Festschreibungs-ID bekannt ist, die Sie entfernen möchten.

git rebase --onto commit-id^ commit-id

Beachten Sie, dass dadurch die durch das Commit eingeführte Änderung tatsächlich entfernt wird.

Rado
quelle
7
Der zusätzliche HEAD in diesem Befehl führt dazu, dass die Rebase mit einem 'abgetrennten HEAD' beendet wird, was unerwünscht ist. Es sollte weggelassen werden.
Frosty
3
Dies kehrt die Änderungen zurück, die meine Commit-ID eingeführt hat. Das OP möchte die Änderungen beibehalten, nur die Commits quetschen.
CB Bailey
1
-1, weil es nicht das tut, was das OP verlangt hat (sondern etwas zerstört, das er ausdrücklich behalten wollte).
Emil Styrke
1
Obwohl es nicht das tut, wonach das OP gefragt hat, war es genau das, was ich brauchte, also +1 für eine nützliche Antwort.
Edvins
20

Um die Antwort von JF Sebastian zu erweitern:

Sie können git-rebase verwenden, um auf einfache Weise alle Arten von Änderungen an Ihrem Commit-Verlauf vorzunehmen.

Nach dem Ausführen von git rebase --interactive erhalten Sie Folgendes in Ihrem $ EDITOR:

pick 366eca1 This has a huge file
pick d975b30 delete foo
pick 121802a delete bar
# Rebase 57d0b28..121802a onto 57d0b28
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

Sie können Zeilen verschieben, um die Reihenfolge der Festschreibungen zu ändern, und Zeilen löschen, um diese Festschreibung zu entfernen. Sie können auch einen Befehl hinzufügen, um zwei Commits zu einem einzigen Commit zu kombinieren (zu quetschen) (vorheriges Commit ist das obige Commit), Commits zu bearbeiten (was geändert wurde) oder Commit-Nachrichten neu zu formulieren.

Ich denke, Pick bedeutet nur, dass Sie dieses Commit in Ruhe lassen wollen.

(Beispiel ist von hier )

idbrii
quelle
14

Sie können B und C in Ihrem Beispiel nicht interaktiv entfernen mit:

git rebase --onto HEAD~5 HEAD~3 HEAD

oder symbolisch,

git rebase --onto A C HEAD

Beachten Sie, dass die Änderungen in B und C nicht in D erfolgen. Sie werden weg sein .

Kopf
quelle
Weitere Informationen finden Sie hier: sethrobertson.github.io/GitFixUm/fixup.html#remove_deep
Max
3

Noch ein Weg,

git rebase -i ad0389efc1a79b1f9c4dd6061dca6edc1d5bb78a (C's hash)
and
git push origin master  -f

Wählen Sie den Hash aus, den Sie als Basis verwenden möchten, und der obige Befehl sollte ihn interaktiv machen, damit Sie alle Top- Nachrichten quetschen können (Sie müssen die ältesten hinterlassen).

Ergebnisweg
quelle
2

Ich finde diesen Prozess viel sicherer und verständlicher, indem ich einen weiteren Zweig aus dem SHA1 von A erstelle und die gewünschten Änderungen auswähle, damit ich sicher bin, dass ich mit dem Aussehen dieses neuen Zweigs zufrieden bin. Danach ist es einfach, den alten Zweig zu entfernen und den neuen umzubenennen.

git checkout <SHA1 of A>
git log #verify looks good
git checkout -b rework
git cherry-pick <SHA1 of D>
....
git log #verify looks good
git branch -D <oldbranch>
git branch -m rework <oldbranch>
Eric Woodruff
quelle
Wenn Sie dies tun, verlieren Sie auch das E-Commit, nicht wahr? Wie ich verstanden habe, löschen Sie den Master und benennen die Nacharbeit in Master um (wenn man bedenkt, dass der ABCDE-Flow der Master-Zweig ist).
Renan Bandeira
1

Habe gerade die Antworten aller Leute gesammelt: (m neu bei git plz benutze es nur als Referenz)

Git Rebase, um alle Commits zu löschen

Git Log

-first check from which commit you want to rebase

Git Rebase -i HEAD ~ 1

-Here i want to rebase on the second last commit- commit count starts from '1')
-this will open the command line editor (called vim editor i guess)

Dann sieht der Bildschirm ungefähr so ​​aus:

wähle 0c2236d Neue Zeile hinzugefügt.

Stellen Sie 2a1cd65..0c2236d auf 2a1cd65 neu her (1 Befehl)

#

Befehle:

p, pick = use commit

r, reword = Commit verwenden, aber die Commit-Nachricht bearbeiten

e, edit = Commit verwenden, aber zum Ändern anhalten

s, squash = Commit verwenden, aber mit dem vorherigen Commit verschmelzen

f, fixup = wie "squash", aber verwerfen Sie die Protokollnachricht dieses Commits

x, exec = Befehl ausführen (der Rest der Zeile) mit Shell

d, drop = Commit entfernen

#

Diese Zeilen können nachbestellt werden. Sie werden von oben nach unten ausgeführt.

#

Wenn Sie hier eine Zeile entfernen, wird dieses Commit verloren gehen.

#

Wenn Sie jedoch alles entfernen, wird die Rebase abgebrochen.

#

Beachten Sie, dass leere Commits auskommentiert werden ~ ~

~
~
~
~
~
~
~
~
~

Ändern Sie hier die erste Zeile nach Ihren Wünschen (verwenden Sie die oben aufgeführten Befehle, z. B. 'drop', um das Commit zu entfernen usw.). Drücken Sie nach Abschluss der Bearbeitung ': x', um den Editor zu speichern und zu beenden (dies gilt nur für den vim-Editor).

Und dann

Git Push

Wenn das Problem angezeigt wird, müssen Sie die Änderungen mit Nachdruck auf Remote übertragen (SEHR KRITISCH: Drücken Sie nicht, wenn Sie im Team arbeiten).

Git Push -f Ursprung

Shrinath Kopare
quelle
-1

Sie können dafür Git Cherry-Pick verwenden. 'cherry-pick' wendet ein Commit auf den Zweig an, auf dem Sie sich gerade befinden.

dann mach

git rebase --hard <SHA1 of A>

Wenden Sie dann die D- und E-Commits an.

git cherry-pick <SHA1 of D>
git cherry-pick <SHA1 of E>

Dadurch wird das B- und C-Commit übersprungen. Allerdings ist es möglicherweise unmöglich, das D-Commit ohne B auf den Zweig anzuwenden, also YMMV.

Rory
quelle
2
Das OP möchte B-, C- und D-Commits kombinieren und ihre Änderungen nicht löschen.
JFS
3
Ich denke du meintest reset --hard, nicht rebase --hard(was nicht existiert)
Mauricio Scheffer