Cofnij część niestazowanych zmian w git

158

Jak mogę cofnąć część moich niestopowanych zmian w git, ale resztę pozostawić jako niestabilną? Sposób, w jaki się zorientowałem, to:

git commit --interactive
# Choose the parts I want to delete
# Commit the changes
git stash
git rebase -i master # (I am an ancestor of master)
# Delete the line of the most recent commit
git stash apply

To działa, ale byłoby miło, gdyby istniało coś takiego, jak git commit --interactivetylko cofanie zmian. Jakieś lepsze metody?

asmeurer
źródło

Odpowiedzi:

264

Możesz użyć git checkout -p, który pozwala wybrać pojedyncze porcje z różnicy między kopią roboczą a indeksem do przywrócenia. Podobnie, git add -ppozwala wybrać porcje do dodania do indeksu i git reset -ppozwala wybrać pojedyncze porcje z różnicy między indeksem a HEAD, aby wycofać się z indeksu.

$ git checkout -p file/to/partially/revert
# or ...
$ git checkout -p .

Jeśli chcesz wcześniej wykonać migawkę repozytorium git, aby zachować te zmiany przed ich przywróceniem, lubię to zrobić:

$ git stash; git stash apply

Jeśli używasz tego często, możesz użyć aliasu:

[alias]
    checkpoint = !git stash; git stash apply

Przywracanie pojedynczych fragmentów lub wierszy może być jeszcze łatwiejsze, jeśli użyjesz dobrego trybu edytora lub wtyczki, która może zapewniać obsługę bezpośredniego wybierania wierszy do przywrócenia, co -pmoże być czasami niezręczne w użyciu. Używam Magit , trybu Emacsa, który jest bardzo pomocny w pracy z Gitem. W Magit możesz uruchomić magit-status, znaleźć różnice dla zmian, które chcesz cofnąć, wybrać linie, które chcesz cofnąć (lub po prostu umieścić kursor na fragmentach, które chcesz przywrócić, jeśli chcesz cofnąć porcję na raz zamiast wiersz na raz) i naciśnij, kaby przywrócić te konkretne wiersze. Bardzo polecam Magit, jeśli używasz Emacsa.

Brian Campbell
źródło
Myślę, że jest to tak skomplikowane, jak rozwiązanie, które wymyśliłem, ale myślę, że moje oryginalne rozwiązanie ma tę zaletę, że zachowuje usunięte linie na 30 dni, ponieważ są one zatwierdzone. Myślę jednak, że może byłoby miło mieć napisany wokół tego skrypt powłoki.
asmeurer
1
Przepraszam, właśnie zdałem sobie sprawę, że git checkoutma również -pflagę, która robi dokładnie to, o co prosiłeś w jednym poleceniu. Przepraszamy za poprzedni złożony zestaw kroków; możesz po prostu użyć git checkout -p. Jeśli chodzi o zapisywanie rzeczy, przed zrobieniem czegoś potencjalnie destrukcyjnego często robię git stash; git stash apply(lub tworzę alias, który robi to jako git checkpointlub coś), aby zapisać bieżące drzewo w skrytce, aby móc do niego wrócić, jeśli coś pójdzie nie tak.
Brian Campbell
Proszę bardzo, to jest rozwiązanie! Jeśli chodzi o alias, to myślę, że coś takiego git commit -a -m "Backup Commit" --edit; git reset HEAD^ byłoby lepsze, bo wtedy nie zabrudziłoby mojego stanu skrytki, którego mogę użyć do czegoś innego. Następnie, jeśli masz SHA1, możesz go wybrać w ciągu następnych 30 dni. --Edit pozwala na dodanie informacji do komunikatu zatwierdzenia, aby pomóc w późniejszym znalezieniu SHA1, jeśli chcesz. Z drugiej strony zabrudziłoby to reflog git, więc myślę, że jest to kompromis oparty na tym, co robisz.
asmeurer
Alias ​​punktu kontrolnego nie działa dla mnie: wykonywane jest tylko pierwsze polecenie, a nie drugie. To smutne, bo bym chciał. Używam git w wersji 1.7.10.4.
Fabien
Czy jest sposób, aby git checkout -pnie stosować poprawek do indeksu, a tylko je wystawiać?
Geremia
28
git diff > patchfile

Następnie edytuj plik poprawki i usuń części, których nie chcesz cofnąć, a następnie:

patch -R < patchfile
octoberblu3
źródło
6
Jednak ta odpowiedź jest szczególnie godna podziwu ze względu na swoją prostotę i łatwość użycia. +1
tholy
Wygenerowany patchfilewygląda świetnie w vimie i naprawdę pomaga!
Bily
Idealny. Potwierdzam, że działa również pod Windows z Cygwin i Ubuntu bash dla Windows.
Checo R
1
Zauważ, że możesz również użyć git apply -Rzamiast patch(tylko po to, aby pozostać w sferze git lub w innym zdarzeniu, które patchnie jest dostępne)
user1556435
patch: **** malformed patch at line 41: @@ -428,9 +443,9 @@ you should place your code here."
HappyFace
5

Mógłbyś

git checkout master -- path/to/file

Dla każdego pliku, który chcesz zresetować.

Drew Hoskins
źródło
Lub użyj HEAD zamiast master, jeśli pracujesz na gałęzi. Jak jest napisane w raporcie „stan gita” (przynajmniej w ostatnich wersjach).
Jonathan Leffler
1

Co powiesz na

  1. Wycofaj pliki, których dotyczy problem, wprowadzając zmiany z indeksu
  2. użyj, git add -paby dodać z powrotem tylko te zmiany, które chcesz, do indeksu.
Abizern
źródło
1

Kiedy uruchamiam „stan git”, wyświetla się:

$ git status
# On branch fr/fr.002
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   makefile
#
no changes added to commit (use "git add" and/or "git commit -a")
$

Tak więc, aby anulować edycje niestacjonarne, każe mi uruchomić:

git checkout -- makefile
Jonathan Leffler
źródło
3
Myślę, że zastanawiał się, jak cofnąć pojedyncze fragmenty, a nie cały plik na raz. Jest to oczywiście rozwiązanie, jeśli chcesz tylko cofnąć wszystkie zmiany w pliku.
Brian Campbell,
Tak - podejrzewam, że masz rację. Opcje „git checkout -p” i „git add -p” wydają się być tym, czego chcemy - jak powiedziałeś.
Jonathan Leffler,
1

Odpowiedź Briana Campbella powoduje awarię mojego gita w wersji 1.9.2.msysgit.0 z nieznanych powodów, więc moje podejście polega na przygotowaniu fragmentów, które chcę zachować, odrzuceniu zmian w kopii roboczej, a następnie usunięciu sceny.

$ git add -p
   ... select the hunks to keep
$ git checkout -- .
$ git reset HEAD .
G-Wiz
źródło
1
Jest też git stash -p. Możesz to zrobić git stash -p; git reset --hard; git stash pop. W praktyce jest to to samo, co robisz, z wyjątkiem tego, że nie musisz pisać wiadomości o zatwierdzeniu.
asmeurer
@asmeurer wiwatuje. Mój nie wymaga komunikatu o zatwierdzeniu, ale prawdopodobnie bardziej sensowne jest użycie do tego skrytki niż indeksu.
G-Wiz
0

możesz to zrobić git checkouti nadać mu nazwy części, które chcesz cofnąć.

Andrzej
źródło