Pytanie Przechowuj tylko jeden plik z wielu plików, które zmieniły się w Git?


Jak mogę przechowywać tylko jeden z wielu zmienionych plików w mojej branży?


2446
2018-06-14 20:52


pochodzenie


Nie sądzę, że zaakceptowana odpowiedź @ bukzor jest poprawną odpowiedzią na zadane pytanie. git stash --keep-index zachowuje indeks, ale przechowuje wszystko - zarówno w indeksie, jak i na zewnątrz. - Raman
@Antonio Wydaje mi się, że twoja nagroda powinna być osobnym pytaniem, ponieważ oryginalne pytanie nie ma nic wspólnego z TortoiseGit. - JesusFreke
@JesusFreke Yep, biorąc pod uwagę wynik, w którym mogłem oszczędzić 50 powtórzeń :) Po prostu jest to pytanie, do którego przekierowujesz, jeśli spróbujesz wyszukać "częściowe ukrywanie tortoisegita". Tortoisegit nie wydaje się być popularnym tematem tutaj stackoverflow.com/questions/tagged/tortoisegit - Antonio
>>>>>>>>> git diff -- *filename* > ~/patch następnie git checkout -- *filename* a później możesz ponownie zastosować łatkę za pomocą git apply ~/patch - neaumusic
Większość istniejących odpowiedzi poniżej są nieaktualne. Od Git 2.13 (Q2 2017) jest obsługiwany przez git stash push [--] [<pathspec>...]. - Ohad Schneider


Odpowiedzi:


Ostrzeżenie

Jak zauważono w komentarzach, wszystko to umieszcza w skrytce, zarówno wystawionej, jak i niezamkniętej. Indeks --keep po prostu pozostawia indeks sam po zakończeniu składowania. Może to powodować konflikty podczas późniejszego otwierania zasobów.


Spowoduje to ukrycie wszystkiego, czego wcześniej nie dodałeś. Właśnie git add rzeczy, które chcesz zachować, a następnie uruchom.

git stash --keep-index

Na przykład, jeśli chcesz podzielić stary commit na więcej niż jeden zestaw zmian, możesz skorzystać z następującej procedury:

  1. git rebase -i <last good commit>
  2. Oznacz niektóre zmiany jako edit.
  3. git reset HEAD^
  4. git add <files you want to keep in this change>
  5. git stash --keep-index
  6. Napraw to, co konieczne. Nie zapomnij git add jakieś zmiany.
  7. git commit
  8. git stash pop
  9. Powtórz, od 5, jeśli to konieczne.
  10. git rebase --continue

1218
2017-11-30 21:28



Uważam, że to podejście jest o wiele prostsze: stackoverflow.com/a/5506483/457268 - k0pernikus
Nie jestem pewien, dlaczego jest to przegłosowane. Każdy musi mieć inne oczekiwania niż ja. Oryginalny post pyta "jak mam przechować tylko część niezatwierdzonych zmian?" Kiedy używam git stash save -k, tak, indeks (zielony w git stat) jest zachowany, ale Cały zestaw zmian (zielony i czerwony) przechodzi do skrytki. To narusza prośbę PO, "przechowuj tylko niektóre zmiany". Chcę ukryć tylko część czerwieni (w przyszłości). - Pistos
Jeśli jesteś bardziej zainteresowany odpowiedzią na pytanie postawione przez @Pistos (tak jak ja), spójrz tutaj: stackoverflow.com/questions/5506339/... - Raman
@Raman: Doskonale! git stash -p jest dokładnie tym, czego szukałem. Zastanawiam się, czy ten przełącznik został niedawno dodany. - Pistos
OSTRZEŻENIE: git stash --keep-index Jest zepsuty. Jeśli wprowadzisz więcej zmian, spróbuj git stash pop później pojawiają się konflikty scalania, ponieważ skrytka zawiera zmienione pliki, które przechowujesz, a nie tylko te, których nie zatrzymałeś. Na przykład: zmieniam pliki A i B, a następnie schowek B, ponieważ chcę przetestować zmiany w A; Znajduję problem z A, który następnie naprawiam; Ja popełniam A; Teraz nie mogę rozpakować, ponieważ stara wersja A znajduje się w skrytce bez żadnego powodu powodując konflikt scalania. W praktyce A i B mogą być wieloma plikami, może nawet obrazami binarnymi lub czymś, więc w zasadzie muszę się poddać i stracić B. - rjmunro


Możesz także użyć git stash save -p "my commit message". W ten sposób możesz wybrać, które porcje powinny zostać dodane do skrytki, można również wybrać całe pliki.

Zostaniesz poproszony o kilka akcji dla każdego przystojniaka:

   y - stash this hunk
   n - do not stash this hunk
   q - quit; do not stash this hunk or any of the remaining ones
   a - stash this hunk and all later hunks in the file
   d - do not stash this hunk or any of the later hunks in the file
   g - select a hunk to go to
   / - search for a hunk matching the given regex
   j - leave this hunk undecided, see next undecided hunk
   J - leave this hunk undecided, see next hunk
   k - leave this hunk undecided, see previous undecided hunk
   K - leave this hunk undecided, see previous hunk
   s - split the current hunk into smaller hunks
   e - manually edit the current hunk
   ? - print help

2619
2017-07-31 11:59



To powinno być na górze, ponieważ bezpośrednio odpowiada na pytanie z najprostszym podejściem. Szkoda, że ​​nie widziałem opcji "a" / "d" (i w pełni przeczytałem tę odpowiedź przed próbą), ponieważ miałem jeden cały plik do dodania do skrytki i inny plik do całkowitego wykluczenia. Mimo to zadziałało dla mnie dobrze; Nie miałem zbyt wielu porcji. (Być może nie zostało to zasugerowane przez nikogo w 2010 roku, kiedy pierwotne pytanie zostało opublikowane, ponieważ ta funkcja nie była wtedy dostępna?) - Liam
Nie było. Został zapożyczony od Darcs, około 7 lat po fakcie. - nomen
Jestem uzależniony od TortoiseGit. Jednak TortoiseGit nie obsługuje stash -p. Nagram tę odpowiedź, ponieważ pozostaje najbardziej interaktywna / przyjazna dla użytkownika. - Antonio
możesz dodać: git stash save -p my stash message; ponieważ kolejność argumenstów nie jest zbyt intuicyjna ... - Chris Maes
Między tym a git log -p, Myślę że -p flaga musi oznaczać "rób to, co chcę, ale nie umiem tego wyrazić". - Kyle Strand


Ponieważ git zasadniczo chodzi o zarządzanie całym repozytorium zadowolony i indeks (a nie jeden lub kilka plików), git stash oferty, nic dziwnego, z całym katalogiem roboczym.

Właściwie, od Git 2.13 (Q2 2017), możesz przechowywać pojedyncze pliki, dzięki:

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

Widzieć "Przechowywanie zmienia się w określone pliki"po więcej.


Oryginalna odpowiedź (poniżej, czerwiec 2010 r.) Dotyczyła ręcznego wybierania tego, co chcesz ukryć.

Casebash komentarze:

Tego stash --patch oryginalne rozwiązanie) jest ładne, ale często zmieniłem wiele plików, więc używanie łatki jest denerwujące

bukzor„s odpowiedź (upvoted, listopad 2011) sugeruje bardziej praktyczne rozwiązanie, oparte na
git add + git stash --keep-index.
Zobacz i przekaż swoją odpowiedź, która powinna być oficjalna (zamiast mojej).

O tej opcji, chhh wskazuje alternatywny przepływ pracy w komentarzach:

powinieneś "git reset --soft"po takim schowku, aby odzyskać jasną inscenizację:
  Aby dostać się do pierwotnego stanu - który jest wyraźnym obszarem postoju i tylko niektórymi wybranymi niefazowymi modyfikacjami, można delikatnie zresetować indeks, aby uzyskać (bez popełnienia czegoś takiego jak ty - bukzor - zrobiłeś).


(Oryginalna odpowiedź z czerwca 2010 r .: ręczne przechowywanie)

Jeszcze, git stash save --patch może umożliwić osiągnięcie częściowego zapasu, którego szukasz:

Z --patch, możesz interaktywnie wybrać porcje z pliku różnic pomiędzy HEAD a drzewem roboczym, które chcesz ukryć.
  Pozycja schowka jest skonstruowana w taki sposób, że jej stan indeksu jest taki sam jak stan indeksu twojego repozytorium, a jego roboczo zawiera tylko zmiany wybrane interaktywnie. Wybrane zmiany zostaną następnie wycofane z twojego worktree.

Jednak spowoduje to zapisanie pełnego indeksu (który może nie być tym, co chcesz, ponieważ może zawierać inne pliki już zindeksowane) i częściowego worktree (który może wyglądać tak, jak ten, który chcesz przechować).

git stash --patch --no-keep-index

może być lepszym rozwiązaniem.


Gdyby --patchnie działa, proces ręczny może:

Dla jednego lub kilku plików pośrednim rozwiązaniem byłoby:

  • skopiuj je poza repozytorium Git
    (Tak właściwie, eleotlecram proponuje interesująca alternatywa)
  • git stash
  • skopiuj je z powrotem
  • git stash # tym razem tylko wybrane pliki są ukryte
  • git stash pop stash@{1} # ponownie zastosować wszystkie modyfikacje plików
  • git checkout -- afile # zresetować plik do treści HEAD, przed lokalnymi modyfikacjami

Pod koniec tego dość kłopotliwego procesu będziesz mieć tylko jeden lub kilka plików ukrytych.


238
2018-06-14 21:23



To jest miłe, ale często zmieniłem wiele plików, więc używanie łatki jest denerwujące - Casebash
@VonC: Dobrze jest mieć tylko jedną odpowiedź na odpowiedź. Ponadto, kopiowanie wklejania cudzych odpowiedzi do własnych jest złe. - bukzor
@ Bukzor: Przykro mi, jeśli moja zredagowana odpowiedź wydawała się niewłaściwa. Moją jedyną intencją było dać twoją odpowiedź bardziej widoczną. Ponownie zredagowałem moje stanowisko, aby uczynić ten zamiar jaśniejszym. - VonC
@Kal: true, stackoverflow.com/a/13941132/6309 sugeruje git reset (mieszany) - VonC
git is fundamentally about managing a all repository content and index and not one or several files - to implementacja, która przyćmiewa rozwiązywany problem; to wyjaśnienie, ale nie usprawiedliwienie. Każdy system kontroli kodu JEST "zarządzać kilkoma plikami". Tylko spójrz, jakie komentarze są najbardziej upvoted. - Victor Sergienko


Gdy git stash -p (lub git add -p z stash --keep-index) byłby zbyt nieporęczny, dzięki czemu łatwiej go używać diff, checkout i apply:

Aby "ukryć" tylko określony plik / katalog:

git diff path/to/dir > stashed.diff
git checkout path/to/dir

Potem potem

git apply stashed.diff

76
2018-02-12 13:44



Ciekawa alternatywa dla git add -p Wspomniałem w mojej własnej odpowiedzi powyżej. +1. - VonC
Zauważ, że jeśli masz pliki binarne (takie jak PNG), nie będą one wyprowadzane do pliku diff. To nie jest rozwiązanie w 100%. - void.pointer
@RobertDailey: To interesujący punkt dla mnie, jako git diff > file.diff i git apply to moje zwykłe narzędzia częściowego ukrywania. Być może będę musiał rozważyć przejście na git stash -p dla większych zestawów zmian. - thekingoftruth
@thekingoftruth Oto alias, którego używam do tworzenia plików łat, i to robi obsługa plików binarnych: patch = log --pretty=email --patch-with-stat --reverse --full-index --binary. Pamiętaj jednak, że to wymaga twoich zmian, aby łatka została zatwierdzona. - void.pointer
To nie działało dla mnie czysto, jeśli plik do przechowania był czymś w rodzaju ../../foo/bar.txt. Łatka generuje OK, ale muszę przejść do katalogu głównego repozytorium, aby łatka mogła zostać zastosowana. Jeśli masz z tym problemy, po prostu upewnij się, że robisz to z katalogu głównego repozytorium. - Michael Anderson


Załóżmy, że masz 3 pliki

a.rb
b.rb
c.rb

i chcesz przechować tylko b.rb i c.rb, ale nie a.rb

możesz zrobić coś takiego

# commit the files temporarily you don't want to stash
git add a.rb
git commit -m "temp" 

# then stash the other files
git stash save "stash message"

# then undo the previous temp commit
git reset --soft HEAD^
git reset

I gotowe! HTH.


43
2017-10-31 07:10





Posługiwać się git stash push, lubię to:

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

Na przykład:

git stash push -- my/file.sh

Jest dostępny od Git 2.13, wydany wiosną 2017 roku.


34
2017-08-15 13:10



Ale wspominam git stash push już w środku moja odpowiedź powyżej w marcu, 5 miesięcy temu. I wyszczególniłem to nowe polecenie Git 2.13 tutaj: stackoverflow.com/a/42963606/6309. - VonC
Cieszę się, że Git rozwija się tak szybko, przez długi czas nie było to możliwe, a następnie wydano 2.13 i nagle dostępne jest proste rozwiązanie! - sandstrom


Inny sposób na zrobienie tego:

# Save everything
git stash 

# Re-apply everything, but keep the stash
git stash apply

git checkout <"files you don't want in your stash">

# Save only the things you wanted saved
git stash

# Re-apply the original state and drop it from your stash
git stash apply stash@{1}
git stash drop stash@{1}

git checkout <"files you put in your stash">

Wpadłem na to po tym, jak (po raz kolejny) przyszedłem na tę stronę i nie podobały mi się dwie pierwsze odpowiedzi (pierwsza odpowiedź po prostu nie odpowiada na pytanie i nie bardzo lubię pracować z -p tryb interaktywny).

Pomysł jest taki sam jak to, co @VonC zasugerował użycie plików spoza repozytorium, zapiszesz zmiany, które chcesz gdzieś, usuniesz zmiany, których nie chcesz w swoim schowku, a następnie ponownie zastosujesz zmiany, które usunąłeś z drogi. Jednak użyłem skrytki git jako "gdzieś" (i na końcu jest jeszcze jeden dodatkowy krok na końcu: usunięcie skrytek, które umieściłeś w skrytce, ponieważ usunąłeś je także z drogi).


25
2018-02-05 10:16



Preferuję to podejście najbardziej. Zapewnia łatwy przepływ pracy w tortoisegit, używając tylko poleceń ukrytych i przywracających. - Mark Ch
Odwoływanie się do odpowiedzi na temat SO z użyciem pozycji nie jest wskazane. Pozycje zmieniają się wraz ze zmianą oceny. - Bryan Ash
@BryanAsh Cóż, tutaj nie ma to znaczenia. Podaję anegdotę, a nie odwołuję się do innych odpowiedzi. Przesłanie jest takie, że nie podobały mi się odpowiedzi, które podobały się społeczności, a nie odpowiedzi. Poza tym luka w liczbie głosów wynosząca 900 pomiędzy drugą a trzecią odpowiedzią sprawia, że ​​jest to mało prawdopodobne, aby zmienić się w najbliższej przyszłości, a jeśli kiedykolwiek się zmieni, zawsze mogę ją edytować, by powiedzieć "najlepsze odpowiedzi". Naprawdę, nie widzę, jak to jest jakikolwiek problem w tej sytuacji. - Jasper


Aktualizacja (14.02.2015) - Przepisałem trochę skrypt, aby lepiej poradzić sobie z przypadkami konfliktów, które teraz powinny być prezentowane jako niezajęte konflikty, a nie pliki .rej.


Często uważam, że bardziej intuicyjne jest odwrócenie podejścia @ bukzora. To znaczy, aby wprowadzić pewne zmiany, a następnie ukryć tylko te zmiany etapowe.

Niestety git nie oferuje skryptu git - only-index lub podobnego, więc przygotowałem skrypt, aby to zrobić.

#!/bin/sh

# first, go to the root of the git repo
cd `git rev-parse --show-toplevel`

# create a commit with only the stuff in staging
INDEXTREE=`git write-tree`
INDEXCOMMIT=`echo "" | git commit-tree $INDEXTREE -p HEAD`

# create a child commit with the changes in the working tree
git add -A
WORKINGTREE=`git write-tree`
WORKINGCOMMIT=`echo "" | git commit-tree $WORKINGTREE -p $INDEXCOMMIT`

# get back to a clean state with no changes, staged or otherwise
git reset -q --hard

# Cherry-pick the index changes back to the index, and stash.
# This cherry-pick is guaranteed to succeed
git cherry-pick -n $INDEXCOMMIT
git stash

# Now cherry-pick the working tree changes. This cherry-pick may fail
# due to conflicts
git cherry-pick -n $WORKINGCOMMIT

CONFLICTS=`git ls-files -u`
if test -z "$CONFLICTS"; then
    # If there are no conflicts, it's safe to reset, so that
    # any previously unstaged changes remain unstaged
    #
    # However, if there are conflicts, then we don't want to reset the files
    # and lose the merge/conflict info.
    git reset -q
fi

Możesz zapisać powyższy skrypt jako git-stash-index gdzieś na twojej ścieżce, a następnie może wywołać go jako indeks ukryty git

# <hack hack hack>
git add <files that you want to stash>
git stash-index

Teraz skrytka zawiera nowy wpis, który zawiera tylko zmiany, które wystawiłeś, a twoje drzewo robocze wciąż zawiera wszelkie zmiany nieskasowane.

W niektórych przypadkach zmiany drzewa roboczego mogą zależeć od zmian indeksu, więc podczas przechowywania zmian w indeksie zmiany drzewa roboczego powodują konflikt. W takim przypadku otrzymasz zwykłe, niezajęte konflikty, które możesz rozwiązać za pomocą git merge / git mergetool / etc.


22
2018-06-16 21:00



Polecić pushd zamiast cd i popd na końcu skryptu, więc jeśli skrypt się powiedzie, użytkownik znajdzie się w tym samym katalogu, co przed uruchomieniem skryptu. - Nate
@Nate: o ile mi wiadomo, powinien zmienić katalog dla użytkownika, jeśli pochodzi ze skryptu. Jeśli uruchomisz skrypt normalnie (~ / bin / git-stash-index) lub przez git (git-stash-index), zostanie uruchomiony w oddzielnej sesji terminalowej, a wszelkie zmiany katalogu roboczego w tej sesji nie będą miały wpływu na katalog roboczy w sesji terminalowej użytkownika. Czy wiesz o powszechnym użyciu, jeśli nie jest to prawda? (inne niż zaopatrzenie scenariusza, którego nie uważam za "powszechny") - JesusFreke


Ponieważ tworzenie gałęzi w Git jest banalne, możesz po prostu utworzyć gałąź tymczasową i sprawdzić poszczególne pliki.


16
2018-06-28 12:07



Nie można utworzyć oddziału z niefasowanymi edycjami. Możesz łatwo przenieść wszystko zmiany w nowej gałęzi (ukrywanie / ukrywanie), ale potem wrócisz do pierwszego: jak przetestujesz swój oddział, wykonując tylko niektóre z tych zmian, nie tracąc innych? - bukzor
Właśnie utworzyłem oddział z niefasowanymi edycjami. - shangxiao
Nie możesz przełącznik oddziałów, jeśli masz lokalne zmiany. Można jednak utworzyć nową gałąź i selektywnie dodawać / zatwierdzać pliki, a następnie tworzyć kolejne gałęzie i wykonywać to samo rekurencyjnie ... następnie sprawdzić oryginalną gałąź i selektywnie scalić z powrotem. Po prostu to zrobiłem. Wydaje się, że to naturalny sposób robienia rzeczy, ponieważ zasadniczo tworzysz gałęzie fabularne. - iain
@Aby móc przełączać gałęzie, jeśli masz lokalne zmiany, o ile nie wymagają scalenia. Widzieć Przykład Gist. Dotyczy to przynajmniej wersji Git v2.7.0. - Colin D Bennett


Na wszelki wypadek odrzucać zmiany za każdym razem, gdy używasz git stash (i tak naprawdę nie używaj git-stash, aby tymczasowo je przechować), w takim przypadku możesz użyć

git checkout -- <file>

[UWAGA]

Że git stash jest szybszą i prostszą alternatywą dla rozgałęzień i robienia rzeczy.


11
2017-12-19 11:40





Zapisz następujący kod w pliku, na przykład o nazwie stash. Używanie jest stash <filename_regex>. Argument jest wyrażeniem regularnym dla pełnej ścieżki pliku. Na przykład, aby zapisać plik a / b / c.txt, stash a/b/c.txt lub stash .*/c.txtitp.

$ chmod +x stash
$ stash .*.xml
$ stash xyz.xml

Kod do skopiowania do pliku:

#! /usr/bin/expect --
log_user 0
set filename_regexp [lindex $argv 0]

spawn git stash -p

for {} 1 {} {
  expect {
    -re "diff --git a/($filename_regexp) " {
      set filename $expect_out(1,string)
    }
    "diff --git a/" {
      set filename ""
    }
    "Stash this hunk " {
      if {$filename == ""} {
        send "n\n"
      } else {
        send "a\n"
        send_user "$filename\n"
      }
    }
    "Stash deletion " {
      send "n\n"
    }
    eof {
      exit
    }
  }
}

10
2017-07-11 18:55



Świetna metoda. Wybrałbym to jako odpowiedź. Wskazówka dla przyszłych czytelników: musisz dopasować się do pełnej ścieżki. na przykład ukryj subdir / foo.c - er0