Зберігати лише поетапні зміни в git - це можливо?


364

Чи є спосіб я приховати лише свої поетапні зміни? Сценарій, з яким виникають проблеми, - це коли я працював над декількома помилками за певний час і мав декілька нестандартних змін. Я хотів би мати змогу поетапно розміщувати ці файли окремо, створювати свої .patch-файли та зберігати їх, поки код не буде затверджено. Таким чином, коли це буде затверджено, я можу зберігати весь (поточний) сеанс, вискакувати цю помилку та натиснути код.

Я йду про це неправильно? Я не розумію, як git може працювати іншими способами, щоб спростити процес?


Так, ви, мабуть, робите не так, щоб потрапити в цю ситуацію. Ще корисне питання. Ви дійсно повинні зберігати чи відгалужувати, перш ніж починати наступне виправлення. Тангенціальна відповідь stackoverflow.com/a/50692885 - це, мабуть, кращий спосіб вирішити це в git. Граючи зі сховищем часто робить дивні речі для моєї робочої зони, якщо я потягнув коміти з верхнього течії.
Самуель Ослунд

Відповіді:


469

Так, це можливо за допомогою ДВОЙНОГО СТАНУ

  1. Постановіть всі свої файли, які потрібно зберегти.
  2. Біжи git stash --keep-index. Ця команда створить приховану скриньку із ВСІМ вашими змінами ( поетапними та нестандартними ), але залишить поетапні зміни у вашому робочому каталозі (все ще знаходиться у стадії стадії).
  3. Біжи git stash push -m "good stash"
  4. Тепер у вас "good stash"є ТІЛЬКИ постановочні файли .

Тепер, якщо вам потрібні нестандартні файли перед схованням, просто застосуйте перший схований файл ( той, створений за допомогою--keep-index ), і тепер ви можете видалити файли, в яких ви зберігали "good stash".

Насолоджуйтесь


Це приховує зміни в субмодулях, навіть якщо вони не є поетапними. Чи є шлях до цього?
rluks

1
це якось залишило всі нові файли (навіть постановочні).
Аурімас

8
@Aurimas, щоб зберігати нові файли, потрібно використовувати -uперемикач.
Гіроміт

2
коли ви повторно застосуєте перший примірник і все зміните назад, тоді як вас можуть зацікавити лише варіанти використання нестабільних змін git stash apply --index. Це спробує зберегти ваше не (поетапне) стан. Простіше видалити небажані зміни з робочого дерева зараз.
otomo

Поки мені не потрібно було робити саме те, що сказала ця відповідь, знаючи про прапор --keep-index було дуже корисно.
Аарон Краус

128

З останнім git ви можете використовувати --patchопцію

git stash push --patch  

git stash save --patch   # for older git versions

І git попросить вас за кожну зміну ваших файлів додавати чи не зберігати.
Ви просто відповісти yабоn

UPD
Алиас для DOUBLE притон :

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

Тепер ви можете налаштувати свої файли, а потім запустити git stash-staged.
В результаті поетапні файли будуть збережені в сховище .

Якщо ви не хочете зберігати поетапні файли та хочете перенести їх у сховище. Потім ви можете додати інший псевдонім і запустити 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"'

17
Технічно не відповідає на питання - але дійсно приємна техніка, яка домагається вибіркового приховування.
alexreardon

6
Погодьтеся, це нормально, але ідея тут із запитанням - Я ВЖЕ ВАШИЙ виконав цю роботу з постановки змін, з якими я хочу щось робити (нібито спочатку робити, але тепер хочу приховати), не шукаючи просто робіть все заново.
Стівен Лу

4
не працює для новостворених файлів (працює лише на змінених файлах)
Дерек Лян

@DerekLiang: Новостворені файли взагалі не відслідковуються. Ймовірно, варто перевірити -u|--include-untrackedваріантgit-stash
Євген Конков

2
З Документів : " save : Цей параметр застаріло на користь git stash push . Він відрізняється від" stash push "тим, що він не може приймати pathspecs, а будь-які необов'язкові аргументи формують повідомлення."
Боржовський

53

Я створив сценарій, який приховує лише те, що наразі ставиться, і залишає все інше. Це дивовижно, коли я починаю робити занадто багато незв'язаних змін. Просто сформулюйте те, що не пов’язано з бажаною фіксацією, і схойте саме це.

(Дякуємо Бартломієві за вихідну точку)

#!/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}

7
Додам, що ви можете перетворити скрипт у команду git, дотримуючись thediscoblog.com/blog/2014/03/29/custom-git-commands-in-3-steps
Петра Бела

3
Це чудово! Я налаштував його, щоб запропонувати користувачеві описати
прихований

1
Це зовсім не працює з git 2.23.0.
thnee

Дякую, я підтримав і перетворив його на псевдонім тут: stackoverflow.com/a/60875067/430128 .
Раман

33

TL; DR Просто додайте -- $(git diff --staged --name-only)для свого <pathspec>параметра git

Ось простий однолінійний:

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

І додати повідомлення просто:

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

Тестовано на v2.17.1 та v2.21.0.windows.1

Обмеження:

  • Будь ласка, майте на увазі, що це буде приховувати кожну річ, якщо у вас немає жодних файлів.
  • Крім того, якщо у вас є файл, який є лише частково поетапним (тобто лише деякі змінені рядки інсценізуються, а деякі інші змінені рядки не є), тоді весь файл буде приховано (включаючи нестандартні рядки).

6
Я думаю, що це найкращий варіант в описуваній ситуації: легко зрозуміти і чорна магія не задіяна!
Луїс

1
Це досить акуратно. Я в кінцевому підсумку створив псевдонім з нього!
Калпеш Панчал

тримати їх голосів прийшов 😉
Somo S.

@KalpeshPanchal можете поділитися своїм псевдонімом? Я не впевнений, як уникнути цього, тому це не правильно трактувати.
Ігор Надж

2
@IgorNadj Звичайно! Ось це: github.com/panchalkalpesh/git-aliases/commit/…
Kalpesh Panchal

15

Зробити те ж саме ...

  1. Постановіть лише ті файли, над якими потрібно працювати.
  2. git commit -m 'temp'
  3. git add .
  4. git stash
  5. git reset HEAD~1

Бум. Файли, які ви не хочете, зберігаються. Файли, які ви хочете, готові для вас.


3
Це легко найкраща відповідь і найлегше запам'ятати
Кевін

9

У цьому сценарії я вважаю за краще створювати нові гілки для кожного випуску. Я використовую temp префікса / тому я знаю, що потім можу видалити ці гілки.

git checkout -b temp/bug1

Постановіть файли, які виправляють bug1, і зафіксуйте їх.

git checkout -b temp/bug2

Тоді ви можете вишнево забрати гроші з відповідних гілок за потребою та подати запит на тягнення.


2
Хоча про приємні приглушені звуки приємно знати, але на практиці це здається підходом, з яким я менш схиляюся.
ryanjdillon

1
Використовуйте "git cherry-pick tmpCommit", щоб повернути тимчасовий фіксатор wo merge-commit або "git merge tmpCommit" + "git reset HEAD ^", щоб отримати зміни без комісій.
Самуель Ослунд

1
Як показує ця відповідь, іноді краще запитати безпосередньо, чого ви хочете досягти, а не як цього досягти за допомогою заданої техніки. Тимчасові гілки та вишня вигідні у складних ситуаціях.
Guney Ozsan

якщо ви частково влаштували файл, вам потрібно буде сховати свої зміни, перш ніж повернутися до початкової гілки та знову
з’явитись

6

Чому б вам не зробити зміни для певної помилки та не створити виправлення з цього комітету та його попередника?

# 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

Потім, щоб створити відповідні патчі, використовуйте git format-patch:

git format-patch HEAD^^

Це створить два файли: 0001-fix-bug-123.patch і0002-fix-bug-321.patch

Або ви можете створити окремі гілки для кожної помилки, тож ви зможете об'єднати або повторно відновити виправлення помилок окремо або навіть видалити їх, якщо вони не спрацюють.


2

git stash --keep-index є гарним рішенням ... окрім того, що він не працював правильно на видалених контурах, що було зафіксовано у Git 2.23 (Q3 2019)

Див. Команду b932f6a (16 липня 2019 року) від Томаса Гаммерера ( tgummerer) .
(Об'єднав Хуніо С Хамано - gitster- в комісії f8aee85 , 25 липня 2019 р.)

stash: виправити обробку видалених файлів за допомогою --keep-index

git stash push --keep-index повинен зберігати всі зміни, додані до індексу, як в індексі, так і на диску.

Наразі це неправильно поводиться, коли файл видалено з індексу.
Замість того, щоб зберігати його видаленим на диску, ** - файл зберігання-індекс зараз відновлює файл. **

Виправте цю поведінку, використовуючи " git checkout" в режимі без накладання, який може вірно відновити індекс і робоче дерево.
Це також спрощує код.

Зауважте, що це замінить незатребувані файли, якщо відстежений файл має те саме ім’я, що і файл, який було видалено в індексі.


2

Зберігати лише індекс (поетапні зміни) у Git складніше, ніж має бути. Я знайшов @ Джо відповіді на роботу добре, і виявився незначним зміна його в цей псевдонім:

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"

Він висуває як поетапні, так і нестандартні зміни у тимчасовий пристрій, залишаючи поетапні зміни в спокої. Потім він підштовхує поетапні зміни до сховища, що є сховищем, яке ми хочемо зберегти. Аргументи, передані псевдоніму, такі як --message "whatever"будуть додані до цієї команди скрипту. Нарешті, він з'являється тимчасовим сховищем для відновлення початкового стану та видаленням тимчасового сховища, а потім, нарешті, "видаляє" сховані зміни з робочого каталогу за допомогою програми зворотного виправлення.

Для протилежної проблеми приховування лише нефіксованих змін (псевдонім stash-working) дивіться цю відповідь .


1

Чи потрібно абсолютно працювати над декількома помилками одночасно? І під "одразу" я маю на увазі "редагування файлів для кількох помилок одночасно". Тому що якщо вам це абсолютно не потрібно, я працюю над однією помилкою одночасно у вашому оточенні. Таким чином, ви можете використовувати локальні гілки & rebase, що мені здається набагато простіше, ніж управління складним сховищем / етапом.

Скажімо, майстер перебуває на команді B. Тепер працюй над помилкою №1.

git checkout -b bug1

Тепер ви знаходитесь у віці помилки1. Внесіть деякі зміни, введіть, чекайте перегляду коду. Це локально, тому ви не зачіпаєте нікого іншого, і зробити патч з git має непросто.

A-B < master
   \
    C < bug1

Тепер ви працюєте над bug2. Повернутися назад до майстра з git checkout master. Зробити нову гілку git checkout -b bug2. Внесіть зміни, введіть, чекайте перегляду коду.

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

Давайте зробимо вигляд, що хтось інший здійснює E&F на майстер, поки ви чекаєте на перегляд.

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

Коли ваш код затверджено, ви можете його відновити для повторного керування, виконавши наступні кроки:

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

Це призведе до наступного:

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

Тоді ви можете натиснути, видалити локальну гілку bu11 і вийти. Помилка на робочому просторі, але, використовуючи локальні гілки, ваш сховище може обробляти кілька помилок. І це дозволяє уникнути складного сценічного та сценічного танцю.

Відповідь на питання ctote в коментарях:

Ну, ви можете повернутися до зберігання для кожної помилки, і працювати тільки з однією помилкою за один раз. Принаймні, це рятує вас від постановки. Але спробувавши це, я особисто вважаю це тривожним. Stashes трохи безладно в графіку журналу git. І що ще важливіше, якщо ви щось накрутили, ви не зможете повернутися. Якщо у вас брудна робоча директорія та ви виправляєте скриньку, її не можна "скасувати". Набагато складніше викрутити вже наявні комісії.

Отже git rebase -i.

Перезавантаживши одну гілку на іншу, ви можете це робити інтерактивно (прапор -i). Коли ви робите це, у вас є можливість вибрати те, що ви хочете робити з кожним комітетом. Pro Git - це дивовижна книга, яка також є в Інтернеті у форматі HTML і має приємний розділ про рисинг та сквош:

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

Я вкраду їхній приклад дослівно для зручності. Зробіть вигляд, що у вас є така історія фіксації, і ви хочете перезавантажити та скасувати помилку1 на майстер:

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

Ось що ви побачите під час введення 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.
#

Щоб скосити всі комісії гілки вниз на одну комісію, збережіть першу фіксацію як "вибір" і замініть усі наступні записи "вибору" на "сквош" або просто "с". Ви також отримаєте можливість змінити повідомлення про фіксацію.

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

Так, так, розчавлення трохи не болить, але я все-таки рекомендую це при сильному використанні сходів.


1
Дякуємо за детальну публікацію! Це точно вирішує багато моїх питань - єдина проблема, яку я бачу, - наша нинішня команда попросила ми продовжувати всі поставки до одного зобов’язання. :(
MrDuk

1
Якщо вони не потребують або хочуть, щоб ваша робоча історія в виробничому репо, це чудово: зробіть свій магістр відслідковування менш застосувавши diff, а не зливши гілки. Ви можете розробити, як зберегти оформлену майстер-гілку, яка має фактично об'єднану історію, і виконати свою справжню роботу з цього, таким чином, автоматичне створення правильних розрізків буде легко автоматизувати.
jthill

2
Зверніть увагу, що git checkout master; git checkout -b bug2їх можна скоротити до git checkout -b bug2 master. Це ж стосується і того git checkout bug1; git rebase master; git checkout master; git merge bug1, що ідентично git rebase master bug1; git push . bug1:master(надано, pushхитрість не очевидна)
knittl

1
Я в основному відповіді ознайомився з підказкою щодо того, щоб прописати вище, щоб я міг використовувати фантазійне форматування
Майк Монкевич

6
Я спростував, тому що це не відповідає на початкове запитання. Я в галузі, яка працює над чимось, і я просто внесла зміни, які, на мою думку, мають бути присвячені окремій галузі інтеграції. Все, що я хочу зробити, - це етап, який змінюють і зберігають його, щоб я міг переключитися на іншу гілку та зробити окремо, замість своєї поточної гілки "незавершеного виробництва". (Попередження. Попереду гнів.) Абсурдно, що це так важко зробити; Я мушу уявити, що це звичайне явище. (Працюючи в одній гілці і помічаючи швидку зміну, яку потрібно зробити, і забувши перейти першим.)
jpmc26

0

З ваших коментарів до відповіді Майка Монкевича я пропоную використовувати більш просту модель: Використовуйте звичайні гілки розробки, але використовуйте сквош-варіант злиття, щоб отримати єдину комісію у вашій головній галузі:

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

Перевага цієї процедури полягає в тому, що ви можете використовувати нормальний робочий потік git.


Я не бачу, як ця відповідь могла б допомогти. Це не пов'язано з оригінальним запитанням.
frapen

0

TL; DR ;git stash-staged

Після створення псевдоніма:

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

Тут git diffповертається список --stagedфайлів. --name-only
Потім ми передаємо цей список як pathspecдля git stashкоматування.

Від 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.


-1

Щоб обрізати випадкову зміну, особливо видалення декількох файлів, виконайте наступне:

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

Іншими словами, схойте лайно і викиньте його разом із сховищем.

Тестовано у git версії 2.17.1


ансамбль без коментарів не допомагає ні мені, ні черговому читачеві ... zaenks бурчання anon. Хоча я можу уявити собі одну проблему з цим однокласником: слід бути дуже обережним, щоб не забути додати всі потрібні зміни до індексу, інакше ці важливі зміни також будуть видалені. Але знову ж таки, необережне використання будь-якого інструменту для кліпів може бути дуже небезпечним для дорогоцінного часу та роботи в гіршому випадку.
wmax
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.