Nur inszenierte Änderungen in Git verstecken - ist das möglich?

366

Gibt es eine Möglichkeit, nur meine inszenierten Änderungen zu speichern? Das Szenario, mit dem ich Probleme habe, ist, wenn ich gleichzeitig an mehreren Fehlern gearbeitet habe und mehrere nicht bereitgestellte Änderungen vorgenommen habe. Ich möchte in der Lage sein, diese Dateien einzeln bereitzustellen, meine .patch-Dateien zu erstellen und sie zu speichern, bis der Code genehmigt ist. Auf diese Weise kann ich, wenn es genehmigt ist, meine gesamte (aktuelle) Sitzung verstecken, diesen Fehler beheben und den Code pushen.

Gehe ich falsch vor? Verstehe ich falsch, wie Git auf andere Weise funktionieren kann, um meinen Prozess zu vereinfachen?

MrDuk
quelle
Ja, Sie machen wahrscheinlich etwas falsch, um in diese Situation zu kommen. Immer noch eine nützliche Frage. Sie sollten sich wirklich verstecken oder verzweigen, bevor Sie mit dem nächsten Fix beginnen. Die tangentiale Antwort stackoverflow.com/a/50692885 ist wahrscheinlich eine bessere Möglichkeit, dies in Git zu handhaben. Das Herumspielen mit dem Vorrat macht meinem Arbeitsbereich oft seltsame Dinge, wenn ich Commits von stromaufwärts gezogen habe.
Samuel Åslund

Antworten:

472

Ja, mit DOUBLE STASH ist das möglich

  1. Stellen Sie alle Ihre Dateien bereit, die Sie verstauen müssen.
  2. Ausführen git stash --keep-index. Dieser Befehl wird ein Versteck mit erstellen ALL Ihren Änderungen ( inszeniert und unstaged ), aber lassen Sie die Änderungen in Ihrem Arbeitsverzeichnis aufgeführt (noch im Zustand inszeniert).
  3. Lauf git stash push -m "good stash"
  4. Jetzt ist Sie "good stash"hat nur inszeniert Dateien .

Wenn Sie vor dem Stash nicht bereitgestellte Dateien benötigen, wenden Sie einfach den ersten Stash an ( den mit--keep-index ), und jetzt können Sie die Dateien entfernen, in denen Sie sich versteckt haben "good stash".

Genießen

Bartłomiej Semańczyk
quelle
Änderungen in Submodulen werden gespeichert, obwohl sie nicht bereitgestellt werden. Gibt es einen Weg, dies zu umgehen?
Rluks
1
Dadurch wurden irgendwie alle neuen Dateien (sogar inszeniert) ausgelassen.
Aurimas
8
@ Aurimas, um neue Dateien zu speichern, müssen Sie den -uSchalter verwenden.
Gyromite
2
Wenn Sie den ersten Stash erneut anwenden und alle Änderungen zurückerhalten, während Sie möglicherweise nur an den Änderungen für die Unage-Änderungen interessiert sind, verwenden Sie die git stash apply --indexOption. Dadurch wird versucht, Ihren nicht (inszenierten) Status beizubehalten. Es ist jetzt einfacher, unerwünschte Änderungen aus dem Arbeitsbaum zu entfernen.
otomo
Obwohl ich nicht genau das tun musste, was diese Antwort sagte, war es sehr hilfreich, das Flag --keep-index zu kennen.
Aaron Krauss
128

Mit dem neuesten Git können Sie die --patchOption verwenden

git stash push --patch  

git stash save --patch   # for older git versions

Und git wird Sie nach jeder Änderung in Ihren Dateien fragen, die Sie in den Stash aufnehmen oder nicht.
Du antwortest einfach yodern

UPD-
Alias ​​für DOUBLE STASH :

git config --global alias.stash-staged '!bash -c "git stash --keep-index; git stash push -m "staged" --keep-index; git stash pop stash@{1}"'

Jetzt können Sie Ihre Dateien bereitstellen und dann ausführen git stash-staged.
Als Ergebnis werden Ihre bereitgestellten Dateien im Stash gespeichert .

Wenn Sie nicht bereitgestellte Dateien behalten und in den Stash verschieben möchten. Dann können Sie einen weiteren Alias ​​hinzufügen und ausführen git move-staged:

git config --global alias.move-staged '!bash -c "git stash-staged;git commit -m "temp"; git stash; git reset --hard HEAD^; git stash pop"'
Eugen Konkov
quelle
17
Technisch beantwortet die Frage nicht - aber eine wirklich schöne Technik, die selektives Verstauen erreicht.
Alexreardon
6
Ich stimme zu, das ist in Ordnung, aber die Idee hier mit der Frage ist, dass ich BEREITS all diese Arbeit getan habe, um die Änderungen zu inszenieren, mit denen ich etwas tun möchte (angeblich ursprünglich, um sie zu begehen, aber jetzt zu verstecken), ohne nur darauf zu achten mach das alles noch einmal.
Steven Lu
4
funktioniert nicht für neu erstellte Dateien (funktioniert nur für die geänderten Dateien)
Derek Liang
@DerekLiang: Neu erstellte Dateien werden überhaupt nicht verfolgt. Sie sollten wahrscheinlich die -u|--include-untrackedOption vongit-stash
Eugen Konkov
2
Aus den Dokumenten : " Speichern : Diese Option ist zugunsten von Git Stash Push veraltet . Sie unterscheidet sich von 'Stash Push' darin, dass sie keine Pfadspezifikationen annehmen kann und keine Argumente ohne Option die Nachricht bilden."
Borjovsky
53

Ich habe ein Skript erstellt, das nur das versteckt, was gerade inszeniert ist, und alles andere hinterlässt. Das ist großartig, wenn ich anfange, zu viele nicht verwandte Änderungen vorzunehmen. Stellen Sie einfach fest, was nicht mit dem gewünschten Commit zusammenhängt, und verstauen Sie genau das.

(Danke an Bartłomiej für den Startpunkt)

#!/bin/bash

#Stash everything temporarily.  Keep staged files, discard everything else after stashing.
git stash --keep-index

#Stash everything that remains (only the staged files should remain)  This is the stash we want to keep, so give it a name.
git stash save "$1"

#Apply the original stash to get us back to where we started.
git stash apply stash@{1}

#Create a temporary patch to reverse the originally staged changes and apply it
git stash show -p | git apply -R

#Delete the temporary stash
git stash drop stash@{1}
Joe
quelle
7
Ich würde hinzufügen, dass Sie das Skript in einen Git-Befehl verwandeln können, indem Sie thediscoblog.com/blog/2014/03/29/custom-git-commands-in-3-steps
Petr Bela
3
Das ist toll! Ich habe es optimiert, um den Benutzer zur Eingabe einer Stash-Beschreibung aufzufordern,
brookinc
1
Dies funktioniert mit Git 2.23.0 überhaupt nicht.
Knie
Vielen Dank, ich habe es positiv bewertet und hier in einen Alias ​​umgewandelt: stackoverflow.com/a/60875067/430128 .
Raman
34

TL; DR Fügen -- $(git diff --staged --name-only)Sie einfach Ihren Git- <pathspec>Parameter hinzu

Hier ist ein einfacher Einzeiler:

git stash -- $(git diff --staged --name-only)

Und um einfach eine Nachricht hinzuzufügen:

git stash push -m "My work in progress" -- $(git diff --staged --name-only)

Getestet auf v2.17.1 und v2.21.0.windows.1

Einschränkungen:

  • Bitte beachten Sie, dass dies jede einzelne Sache verstaut, wenn Sie keine Dateien bereitgestellt haben.
  • Auch wenn Sie eine Datei haben, die nur teilweise bereitgestellt wird (dh nur einige geänderte Zeilen werden bereitgestellt, während andere geänderte Zeilen nicht bereitgestellt werden), wird die gesamte Datei (einschließlich nicht bereitgestellter Zeilen) gespeichert.
Somo S.
quelle
6
Ich denke, dies ist die beste Option für die beschriebene Situation: leicht zu verstehen und ohne schwarze Magie!
Luis
1
Das ist ziemlich ordentlich. Am Ende habe ich einen Alias ​​daraus erstellt!
Kalpesh Panchal
Keep 'em Votes Comin Som
Somo S.
@ KalpeshPanchal können Sie Ihren Alias ​​teilen? Ich bin mir nicht sicher, wie ich dem entkommen soll, daher wird es nicht richtig interpretiert.
Igor Nadj
2
@IgorNadj Sicher! Hier ist es: github.com/panchalkalpesh/git-aliases/commit/…
Kalpesh Panchal
15

Um das Gleiche zu erreichen ...

  1. Stellen Sie nur die Dateien bereit, an denen Sie arbeiten möchten.
  2. git commit -m 'temp'
  3. git add .
  4. git stash
  5. git reset HEAD~1

Boom. Die Dateien, die Sie nicht möchten, werden gespeichert. Die gewünschten Dateien sind alle für Sie bereit.

Michael
quelle
3
Dies ist leicht die beste Antwort und die am leichtesten zu merkende
Kevin
9

In diesem Szenario ziehe ich es vor, für jede Ausgabe neue Zweige zu erstellen. Ich verwende ein Präfix temp /, damit ich weiß, dass ich diese Zweige später löschen kann.

git checkout -b temp/bug1

Stellen Sie die Dateien bereit, die bug1 beheben, und schreiben Sie sie fest.

git checkout -b temp/bug2

Sie können dann die Commits nach Bedarf aus den jeweiligen Filialen auswählen und eine Pull-Anfrage senden.

Shamps
quelle
2
Während es gut ist, ausgefallene Stashing-Sounds zu kennen, scheint dies in der Praxis ein Ansatz zu sein, bei dem ich weniger wahrscheinlich bin.
Ryanjdillon
1
Verwenden Sie "git cherry-pick tmpCommit", um das temporäre Commit für ein Merge-Commit zurückzugewinnen, oder "git merge tmpCommit" + "git reset HEAD ^", um die Änderungen ohne das Commit zu erhalten.
Samuel Åslund
1
Wie diese Antwort manchmal zeigt, ist es besser, direkt zu fragen, was Sie erreichen möchten, als wie Sie es mit einer bestimmten Technik erreichen. Temporäre Zweige und Kirschbaum sind in komplizierten Situationen praktisch.
Guney Ozsan
Wenn Sie eine Datei teilweise
bereitgestellt haben
6

Warum übernehmen Sie die Änderung nicht für einen bestimmten Fehler und erstellen einen Patch aus diesem Commit und seinem Vorgänger?

# hackhackhack, fix two unrelated bugs
git add -p                   # add hunks of first bug
git commit -m 'fix bug #123' # create commit #1
git add -p                   # add hunks of second bug
git commit -m 'fix bug #321' # create commit #2

Verwenden Sie dann zum Erstellen der entsprechenden Patches git format-patch:

git format-patch HEAD^^

Dadurch werden zwei Dateien erstellt: 0001-fix-bug-123.patchund0002-fix-bug-321.patch

Oder Sie können für jeden Fehler separate Zweige erstellen, um Fehlerkorrekturen einzeln zusammenzuführen oder neu zu definieren oder sie sogar zu löschen, wenn sie nicht funktionieren.

stricken
quelle
2

git stash --keep-index ist eine gute Lösung ... außer dass es auf entfernten Pfaden nicht richtig funktioniert hat, was in Git 2.23 (Q3 2019) behoben wurde.

Siehe Commit b932f6a (16. Juli 2019) von Thomas Gummerer ( tgummerer) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit f8aee85 , 25. Juli 2019)

stash: Fix Umgang mit entfernten Dateien mit --keep-index

git stash push --keep-index soll alle Änderungen, die dem Index hinzugefügt wurden, sowohl im Index als auch auf der Festplatte behalten.

Derzeit verhält sich dies nicht richtig, wenn eine Datei aus dem Index entfernt wird.
Anstatt es auf der Festplatte gelöscht zu halten, stellt ** - keep-index die Datei derzeit wieder her. **

Beheben Sie dieses Verhalten, indem Sie ' git checkout' im No-Overlay-Modus verwenden, um den Index und den Arbeitsbaum originalgetreu wiederherzustellen.
Dies vereinfacht auch den Code.

Beachten Sie, dass dadurch nicht verfolgte Dateien überschrieben werden, wenn die nicht verfolgte Datei denselben Namen hat wie eine Datei, die im Index gelöscht wurde.

VonC
quelle
2

Es ist schwieriger, nur den Index (inszenierte Änderungen) in Git zu speichern, als es sein sollte. Ich habe @ Joes Antwort gefunden , um gut zu funktionieren, und eine kleine Variation davon in diesen Alias ​​umgewandelt:

stash-index = "!f() { \
  git stash push --quiet --keep-index -m \"temp for stash-index\" && \
  git stash push \"$@\" && \
  git stash pop --quiet stash@{1} && \
  git stash show -p | git apply -R; }; f"

Es verschiebt sowohl die inszenierten als auch die nicht inszenierten Änderungen in einen temporären Speicher, wobei die inszenierten Änderungen in Ruhe gelassen werden. Anschließend werden die inszenierten Änderungen in den Stash verschoben, den wir behalten möchten. An den Alias ​​übergebene Argumente, wie --message "whatever"sie diesem Stash-Befehl hinzugefügt werden. Schließlich wird der temporäre Stash geöffnet, um den ursprünglichen Zustand wiederherzustellen und den temporären Stash zu entfernen. Anschließend werden die versteckten Änderungen über eine Reverse-Patch-Anwendung aus dem Arbeitsverzeichnis "entfernt".

Für das entgegengesetzte Problem, nur die nicht bereitgestellten Änderungen (Alias stash-working) zu verstecken, siehe diese Antwort .

Raman
quelle
1

Ist es unbedingt notwendig, mehrere Fehler gleichzeitig zu bearbeiten? Und mit "sofort" meine ich "Dateien gleichzeitig für mehrere Fehler bearbeiten lassen". Denn wenn Sie das nicht unbedingt brauchen, würde ich in Ihrer Umgebung jeweils nur an einem Fehler arbeiten. Auf diese Weise können Sie lokale Filialen und Rebase verwenden, was für mich weitaus einfacher ist als die Verwaltung eines komplexen Stashs / ​​einer komplexen Phase.

Angenommen, der Master befindet sich bei Commit B. Arbeiten Sie nun an Fehler 1.

git checkout -b bug1

Jetzt bist du auf Branch Bug1. Nehmen Sie einige Änderungen vor, legen Sie fest und warten Sie auf die Codeüberprüfung. Dies ist lokal, so dass Sie niemanden betreffen, und es sollte einfach genug sein, einen Patch aus Git-Diffs zu erstellen.

A-B < master
   \
    C < bug1

Jetzt arbeiten Sie an Bug2. Geh zurück zum Meister mit git checkout master. Machen Sie einen neuen Zweig , git checkout -b bug2. Nehmen Sie Änderungen vor, legen Sie fest, warten Sie auf die Codeüberprüfung.

    D < bug2
   /
A-B < master
   \
    C < bug1

Stellen wir uns vor, jemand anderes legt E & F für den Master fest, während Sie auf die Überprüfung warten.

    D < bug2
   /
A-B-E-F < master
   \
    C < bug1

Wenn Ihr Code genehmigt wurde, können Sie ihn mit den folgenden Schritten erneut als Master verwenden:

git checkout bug1
git rebase master
git checkout master
git merge bug1

Dies führt zu Folgendem:

    D < bug2
   /
A-B-E-F-C' < master, bug1

Dann können Sie pushen, Ihren lokalen Bug1-Zweig löschen und los geht's. Jeweils ein Fehler in Ihrem Arbeitsbereich, aber bei Verwendung lokaler Zweige kann Ihr Repository mehrere Fehler behandeln. Und das vermeidet einen komplizierten Bühnen- / Stash-Tanz.

Antwort auf die Frage von ctote in den Kommentaren:

Nun, Sie können für jeden Fehler zum Verstecken zurückkehren und jeweils nur mit einem Fehler arbeiten. Zumindest erspart Ihnen das Staging-Problem. Aber nachdem ich das versucht habe, finde ich es persönlich problematisch. Stashes sind in einem Git-Log-Diagramm etwas chaotisch. Und was noch wichtiger ist: Wenn Sie etwas vermasseln, können Sie nicht zurückkehren. Wenn Sie ein schmutziges Arbeitsverzeichnis haben und einen Stash einfügen, können Sie diesen Pop nicht "rückgängig machen". Es ist viel schwieriger, bereits vorhandene Commits zu vermasseln.

Also git rebase -i.

Wenn Sie einen Zweig auf einen anderen umstellen, können Sie dies interaktiv tun (das Flag -i). Wenn Sie dies tun, haben Sie die Möglichkeit, auszuwählen, was Sie mit jedem Commit tun möchten. Pro Git ist ein großartiges Buch, das auch im HTML-Format online ist und einen schönen Abschnitt über Rebasing & Squashing enthält:

http://git-scm.com/book/ch6-4.html

Ich werde ihr Beispiel der Einfachheit halber wörtlich stehlen. Stellen Sie sich vor, Sie haben den folgenden Commit-Verlauf und möchten bug1 auf master neu starten und quetschen:

    F < bug2
   /
A-B-G-H < master
   \
    C-D-E < bug1

Folgendes sehen Sie, wenn Sie tippen git rebase -i master bug1

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Um alle Commits eines Zweigs zu einem einzigen Commit zusammenzufassen, behalten Sie das erste Commit als "pick" bei und ersetzen Sie alle nachfolgenden "pick" -Einträge durch "squash" oder einfach "s". Sie haben auch die Möglichkeit, die Commit-Nachricht zu ändern.

pick f7f3f6d changed my name a bit
s 310154e updated README formatting and added blame
s a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

Also ja, Quetschen ist ein bisschen schmerzhaft, aber ich würde es trotzdem empfehlen, wenn man viel Stashes benutzt.

Mike Monkiewicz
quelle
1
Danke für den ausführlichen Beitrag! Dies löst mit Sicherheit viele meiner Probleme - das einzige Problem, das ich sehe, ist, dass unser derzeitiges Team darum gebeten hat, dass wir alle Lieferungen auf ein einziges Commit beschränken. :(
MrDuk
1
Wenn sie Ihren Arbeitsverlauf im Produktions-Repo nicht benötigen oder wollen, ist das in Ordnung: Machen Sie Ihren Tracking-Master-Verlauf ohne Verlauf, indem Sie Unterschiede anwenden, anstatt Zweige zusammenzuführen. Sie können herausfinden, wie Sie einen verzierten Hauptzweig mit dem tatsächlichen zusammengeführten Verlauf beibehalten und daraus Ihre eigentliche Arbeit erledigen können. Auf diese Weise können Sie die Generierung der richtigen Unterschiede einfach automatisieren.
Bis zum
2
Beachten Sie, dass git checkout master; git checkout -b bug2auf gekürzt werden kann git checkout -b bug2 master. Gleiches gilt für git checkout bug1; git rebase master; git checkout master; git merge bug1, was identisch ist git rebase master bug1; git push . bug1:master( push
zugegeben
1
Ich gab eine exemplarische Vorgehensweise für das Verstauen oben in der Hauptantwort, damit ich ausgefallene Formatierungen verwenden konnte
Mike Monkiewicz
6
Ich habe abgestimmt, weil dies die ursprüngliche Frage nicht beantwortet. Ich bin in einer Zweigstelle und arbeite an etwas. Ich habe gerade eine Änderung vorgenommen, die meiner Meinung nach separat für die Integrationsbranche gelten sollte. Alles, was ich tun möchte, ist, diese Änderung zu inszenieren und zu speichern, damit ich zu einem anderen Zweig wechseln und separat festschreiben kann, anstatt zu meinem aktuellen Zweig "In Bearbeitung". Es ist absurd, dass dies so schwer zu tun ist. Ich muss mir vorstellen, dass dies häufig vorkommt . (Arbeiten in einem Zweig und Erkennen einer schnellen Änderung, die vorgenommen werden muss, und Vergessen, zuerst zu wechseln.)
jpmc26
0

Aus Ihren Kommentaren zur Antwort von Mike Monkiewicz schlage ich vor, ein einfacheres Modell zu verwenden: Verwenden Sie reguläre Entwicklungszweige, aber verwenden Sie die Squash-Option der Zusammenführung, um ein einzelnes Commit in Ihrem Hauptzweig zu erhalten:

git checkout -b bug1    # create the development branch
* hack hack hack *      # do some work
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
git checkout master     # go back to the master branch
git merge --squash bug1 # merge the work back
git commit              # commit the merge (don't forget
                        #    to change the default commit message)
git branch -D bug1      # remove the development branch

Der Vorteil dieses Verfahrens besteht darin, dass Sie den normalen Git-Workflow verwenden können.

Rudi
quelle
Ich kann nicht sehen, wie diese Antwort helfen könnte. Es bezieht sich nicht auf die ursprüngliche Frage.
Frapen
0

TL; DR ;git stash-staged

Nach dem Erstellen eines Alias:

git config --global alias.stash-staged '!bash -c "git stash -- \$(git diff --staged --name-only)"'

Hier wird eine git diffListe der --stagedDateien zurückgegeben --name-only
. Dann übergeben wir diese Liste pathspecan git stashcommad.

Von man git stash:

git stash [--] [<pathspec>...]

<pathspec>...
   The new stash entry records the modified states only for the files
   that match the pathspec. The index entries and working tree
   files are then rolled back to the state in HEAD only for these
   files, too, leaving files that do not match the pathspec intact.

Eugen Konkov
quelle
-1

Gehen Sie wie folgt vor, um eine versehentliche Änderung, insbesondere das Löschen mehrerer Dateien, zu bereinigen:

git add <stuff to keep> && git stash --keep-index && git stash drop

Mit anderen Worten, verstauen Sie den Mist und werfen Sie ihn zusammen mit dem Versteck weg.

Getestet in Git Version 2.17.1

wmax
quelle
Eine Ablehnung ohne Kommentar ist weder für mich noch für den nächsten Leser hilfreich ... zaenks mürrisch anon. Obwohl ich mir ein Problem mit diesem Einzeiler vorstellen kann: Man sollte sehr vorsichtig sein, um nicht zu vergessen, alle gewünschten Änderungen zum Index hinzuzufügen, da sonst diese wichtigen Änderungen ebenfalls gelöscht werden. Andererseits kann die unachtsame Verwendung eines Cli-Tools im schlimmsten Fall sehr gefährlich für die kostbare Zeit und Arbeit sein.
Wmax