Копить только один файл из нескольких файлов, которые были изменены до git 2.13

2021

Как я могу занести только один из нескольких измененных файлов в моей ветке?

  • 95
    Я не думаю, что принятый ответ @ bukzor является правильным ответом на вопрос, который был задан. git stash --keep-index сохраняет индекс, но сохраняет все - как в индексе, так и вне его.
  • 0
    @Antonio Мне кажется, что ваша награда должна быть отдельным вопросом, так как исходный вопрос не имеет ничего общего с TortoiseGit.
Показать ещё 4 комментария
Теги:
git-stash

24 ответа

1116
Лучший ответ

Внимание

Как отмечено в комментариях, это ставит все в тайник, как поставленный, так и нестационарный. Индекс -keep просто оставляет индекс только после того, как тайник будет выполнен. Это может привести к конфликтам слияния, когда вы позже поместите тайник.


Это закроет все, что вы ранее не добавляли. Просто git add то, что вы хотите сохранить, затем запустите его.

git stash --keep-index

Например, если вы хотите разбить старую фиксацию на несколько наборов изменений, вы можете использовать эту процедуру:

  • git rebase -i <last good commit>
  • Отметьте некоторые изменения как edit.
  • git reset HEAD^
  • git add <files you want to keep in this change>
  • git stash --keep-index
  • При необходимости исправить вещи. Не забывайте git add любые изменения.
  • git commit
  • git stash pop
  • При необходимости повторите, начиная с № 5.
  • git rebase --continue
  • 0
    Я использовал это, чтобы разделить некоторые изменения, которые не были связаны, используя rebase -i, и это работало хорошо. Я добавил файлы, над которыми хотел поработать, прятал, как указано выше, исправлял вещи по мере необходимости, коммитил, прятал всплывающие окна и повторял.
  • 0
    да, я только что заметил, что для того, чтобы перейти в исходное состояние - это чистая промежуточная область и только с некоторыми выбранными неизмененными модификациями, можно мягко сбросить индекс, чтобы получить (не совершая ничего, как вы сделали). неважно, и спасибо за информацию! :)
Показать ещё 15 комментариев
2338

Вы также можете использовать git stash -p. Таким образом, вы можете выбрать, какие блоки должны быть добавлены в stash, можно также выбрать целые файлы.

Вам будет предложено несколько действий для каждого столбца:

   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
  • 5
    Не было. Он был заимствован у Дарка примерно через 7 лет после свершившегося факта.
  • 0
    @MatthieuNapoli ты уверен? Это действительно быстро, и помощь появляется вверху. q (выход) - не спрятать текущий ханк или любой оставшийся a (все) - этот ханк, и все те, что после d (сделано с этим файлом) - не спрятать этот ханк, или все, что осталось в этом файле / (поиск) - поиск фрагмента e (редактирование) - редактирование текущего фрагмента вручную
Показать ещё 8 комментариев
153

Поскольку git принципиально относится к управлению содержимым и индексом репозитория (а не одним или несколькими файлами), git stash заключает сделки, не удивительно, со всем рабочим каталогом.

На самом деле, поскольку git 2.13 (Q2 2017), вы можете сохранять отдельные файлы, используя:

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

Подробнее см. "Изменения в файлах".


Первоначальный ответ (ниже, июнь 2010 г.) касался выбора вручную того, что вы хотите сохранить.

Casebash комментарии:

Это (оригинальное решение stash --patch) приятно, но часто я модифицировал много файлов, поэтому использование патча раздражает

bukzor answer (upvoted, ноябрь 2011 г.) предлагает более практичное решение, основанное на < ш > git add + git stash --keep-index.
Пойдите, посмотрите и подтвердите свой ответ, который должен быть официальным (вместо моего).

Об этой опции chhh указывает на альтернативный рабочий процесс в комментариях:

вам следует "git reset --soft" после такого заноса, чтобы получить четкую постановку назад:
Чтобы попасть в исходное состояние - это четкая зона постановки и только с некоторыми выборочными неустановленными модификациями, можно было бы мягко reset получить индекс (без совершения чего-либо подобного вам - bukzor - did).


(Первоначальный ответ июнь 2010 года: ручной тайник)

Тем не менее, git stash save --patch может позволить вам выполнить частичное списание после:

С помощью --patch вы можете в интерактивном режиме выбирать группы в разделе diff между HEAD и рабочим деревом, которое нужно спрятать.
Запись stash сконструирована так, что ее состояние индекса совпадает с состоянием индекса вашего репозитория, а его рабочая строка содержит только те изменения, которые вы выбрали в интерактивном режиме. Затем выбранные изменения откатываются из вашей рабочей строки.

Однако это сохранит полный индекс (который может и не быть тем, что вы хотите, поскольку он может включать другие уже индексированные файлы), и частичную рабочую строку (которая может выглядеть как та, которую вы хотите зашить).

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

может быть лучше.


Если --patch не работает, ручной процесс может:

Для одного или нескольких файлов промежуточное решение будет состоять в следующем:

  • скопируйте их вне репозитория git
    (На самом деле eleotlecram предлагает интересную альтернативу)
  • git stash
  • скопируйте их.
  • git stash # на этот раз, только файлы, которые вы хотите, спрятаны
  • git stash pop stash@{1} # повторно применить все изменения ваших файлов.
  • git checkout -- afile # reset файл к содержимому HEAD перед любыми локальными изменениями

В конце этого довольно громоздкого процесса у вас будет только один или несколько файлов.

  • 3
    Это хорошо, но часто я модифицировал много файлов, поэтому использование патча раздражает
  • 0
    @ Casebash: Тогда посмотрите мой ответ ниже.
Показать ещё 8 комментариев
69

Если git stash -p (или git add -p с stash --keep-index) было бы слишком громоздким, мне было проще использовать diff, checkout и apply:

Чтобы "зашить" конкретный файл/только dir:

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

Затем после

git apply stashed.diff
  • 1
    Интересная альтернатива git add -p я упомянул в своем ответе выше. +1.
  • 11
    Обратите внимание, что если у вас есть двоичные файлы (например, PNG), они не будут выводиться в файл diff. Так что это не 100% решение.
Показать ещё 3 комментария
39

Скажем, у вас есть 3 файла

a.rb
b.rb
c.rb

и вы хотите сохранить только b.rb и c.rb, но не a.rb

вы можете сделать что-то вроде этого

# 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

И все готово! НТН.

22

Другой способ сделать это:

# 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">

Я придумал это после того, как я (еще раз) пришел на эту страницу и мне не понравились первые два ответа (первый ответ просто не отвечает на вопрос, и мне не очень нравилось работать с -p интерактивный режим).

Идея такая же, как и то, что @VonC предложила использовать файлы вне репозитория, вы сохраняете необходимые изменения где-то, удаляете изменения, которые не хотите в своем кошельке, а затем повторно применяете изменения, которые вы переместили из путь. Тем не менее, я использовал git stash как "где-то" (и, как результат, один дополнительный шаг в конце: удаление cahnges, которые вы помещаете в stash, потому что вы также отбросили их).

  • 1
    я предпочитаю такой подход больше всего. Это обеспечивает легкий рабочий процесс в tortoisegit, используя только команды stash и revert.
  • 0
    Ссылка на ответы по SO с использованием позиций не рекомендуется. Позиции меняются по мере изменения рейтингов.
Показать ещё 1 комментарий
22

Обновление (2/14/2015). Я немного перезаписал script, чтобы лучше справляться с ситуациями конфликтов, которые теперь должны быть представлены как конфликты без взаимодействия, а не с файлами .rej.


Мне часто кажется более интуитивным делать обратный подход @bukzor. То есть, чтобы внести некоторые изменения, а затем занести только эти поэтапные изменения.

К сожалению, git не предлагает git stash --only-index или аналогичный, поэтому я взломал script, чтобы сделать это.

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

Вы можете сохранить приведенный выше script как git-stash-index где-то на своем пути и затем вызывать его как git stash-index

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

Теперь в stash содержится новая запись, в которой содержатся только те изменения, которые вы поставили, а ваше рабочее дерево все еще содержит любые неустановленные изменения.

В некоторых случаях изменения рабочего дерева могут зависеть от изменений индекса, поэтому, когда вы меняете изменения индекса, изменения рабочего дерева имеют конфликт. В этом случае вы получите обычные конфликты без конфликтов, которые вы можете разрешить с помощью git merge/git mergetool/etc.

  • 0
    Рекомендуйте pushd вместо cd и popd в конце скрипта, чтобы в случае успеха скрипта пользователь оказался в том же каталоге, что и до его запуска.
  • 1
    @Nate: насколько я знаю, он должен изменять каталог для пользователя только в том случае, если он использует сценарий. Если вы запускаете скрипт нормально (~ / bin / git-stash-index) или через git (git stash-index), он запускается в отдельном терминальном сеансе, и любые изменения рабочего каталога в этом сеансе не влияют на рабочий каталог в терминальной сессии пользователя. Знаете ли вы о частом случае использования, когда это не так? (кроме поиска сценария, который я бы не назвал «обычным»)
15

Поскольку создание ветвей в Git тривиально, вы можете просто создать временную ветвь и проверить в ней отдельные файлы.

  • 2
    Вы не можете создать ветку с неустановленными изменениями. Вы можете легко переместить все изменения в новую ветвь (stash / stash pop), но затем вы вернетесь к исходной точке: как вы тестируете свою ветку только с некоторыми из этих правок, не теряя другие?
  • 0
    Я только что создал ветку с неустановленными правками.
Показать ещё 2 комментария
13

Используйте git stash push, например:

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

Например:

git stash push -- my/file.sh

Это доступно с Git 2.13, выпущенным в spring 2017.

  • 1
    Но я упоминаю git stash push в своем ответе выше в марте прошлого года, 5 месяцев назад. И я подробно описал эту новую команду Git 2.13 здесь: stackoverflow.com/a/42963606/6309 .
  • 0
    Я рад, что Git продвигается так быстро, долгое время это было невозможно, а затем была выпущена версия 2.13, и внезапно стало доступно простое решение!
Показать ещё 2 комментария
10

Сохраните следующий код в файле, например, с именем stash. Использование stash <filename_regex>. Аргумент - это регулярное выражение для полного пути файла. Например, чтобы запереть a/b/c.txt, stash a/b/c.txt или stash .*/c.txt и т.д.

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

Код для копирования в файл:

#! /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
    }
  }
}
  • 2
    Отличный метод. Я бы выбрал это в качестве ответа. Совет для будущих читателей: вы должны соответствовать на полном пути. например, stash subdir / foo.c
9

На всякий случай вы на самом деле означаете "отменить изменения" всякий раз, когда вы используете "git stash" (и на самом деле не используйте git stash для временного хранения), в этом случае вы можете использовать

git checkout -- <file>

Обратите внимание, что git stash - это просто более простая и простая альтернатива ветвлению и работе.

7

Проблема с "промежуточным" решением VonC по копированию файлов вне репозитория Git заключается в том, что вы теряете информацию о пути, что делает копирование кучи файлов позже более сложным.

Найдите легче использовать tar (аналогичные инструменты, вероятно, сделают) вместо копии:

  • tar cvf/tmp/stash.tar path/to/some/путь к файлу/в/some/other/file (... и т.д.)
  • git checkout path/to/some/путь к файлу/в/some/other/file
  • git stash
  • tar xvf/tmp/stash.tar
  • и т.д.. (см. предложение VonC "промежуточное" )
  • 0
    проверка -f пугает меня
  • 0
    checkout -f не нужен, checkout (без -f ) достаточно, я обновил ответ.
6

Каждый ответ здесь настолько сложный...

Как насчет этого: "stash":

git diff /dir/to/file/file_to_stash > /tmp/stash.patch
git checkout -- /dir/to/file/file_to_stash

Чтобы вернуть файл обратно:

git apply /tmp/stash.patch

Точное совпадение с тем, как скопировать один файл и снова вставить его.

  • 0
    Я попробовал, но ничего не происходит. Когда я git apply у меня нет ошибки, но изменения не возвращаются ни
  • 0
    Файл патча, сгенерированный вами в / tmp, вероятно, был удален. Возможно, вы перезагрузились между diff и apply. Попробуйте другое более постоянное местоположение. Это работает. Также проверьте содержимое файла патча.
6

Это можно сделать легко в 3 этапа с помощью SourceTree.

  • Временно зафиксировать все, что вы не хотите спрятать.
  • Git добавить все остальное, а затем сохранить его.
  • Введите временную фиксацию, запустив git reset, нацелив фиксацию до вашего временного.

Все это можно сделать в течение нескольких секунд в SourceTree, где вы можете просто щелкнуть по файлам (или даже отдельным строкам), которые вы хотите добавить. После добавления просто привяжите их к временному фиксации. Затем установите флажок, чтобы добавить все изменения, а затем нажмите кнопку "Скрыть", чтобы сохранить все. С укороченными изменениями взгляните на свой список фиксаций и отметьте хеш для фиксации перед вашей временной фиксацией, затем запустите "git reset hash_b4_temp_commit", что в основном напоминает "выскакивание" фиксации путем сброса ваша ветка к фиксации прямо перед ней. Теперь у вас осталось только то, что вы не хотели спрятать.

6

Иногда я делал несвязанные изменения в своей ветке, прежде чем я ее совершил, и я хочу переместить ее в другую ветку и передать ее отдельно (например, мастер). Я делаю это:

git stash
git checkout master
git stash pop
git add <files that you want to commit>
git commit -m 'Minor feature'
git stash
git checkout topic1
git stash pop
...<resume work>...

Обратите внимание, что первые stash и stash pop могут быть устранены, вы можете переносить все свои изменения на ветвь master при проверке, но только в случае отсутствия конфликтов. Также, если вы создаете новую ветку для частичных изменений, вам понадобится тайник.

Вы можете упростить его при отсутствии конфликтов и новой ветки:

git checkout master
git add <files that you want to commit>
git commit -m 'Minor feature'
git checkout topic1
...<resume work>...

Штамм даже не нужен...

3

Я бы использовал git stash save --patch. Я не считаю интерактивность раздражающей, потому что во время нее есть варианты, чтобы применить желаемую операцию к целым файлам.

  • 3
    Удивлен, что для этого ответа так мало поддержки, это лучшее решение без необходимости написания эссе.
  • 0
    Определенно хороший ответ, git stash -p позволяет вам быстро сохранить весь файл и выйти из него.
2

В этой ситуации я git add -p (интерактивный), git commit -m blah, а затем спрятать оставшееся при необходимости.

1

Я рассмотрел ответы и комментарии для этого и ряд подобных потоков. Имейте в виду, что ни одна из следующих команд не является правильной для того, чтобы иметь возможность фиксировать любые определенные отслеживаемые/не проверенные файлы:

  • git stash -p (--patch): выберите hunks вручную, исключая невоспроизводимые файлы
  • git stash -k (--keep-index): запишите все отслеживаемые/невоспроизводимые файлы и сохраните их в рабочем каталоге
  • git stash -u (--include-untracked): сохранить все отслеживаемые/невоспроизводимые файлы
  • git stash -p (--patch) -u (--include-untracked): неверная команда

В настоящее время наиболее разумным методом, позволяющим записывать любые файлы с отслеживанием/без следа, является:

  • Временно зафиксируйте файлы, которые вы не хотите сохранять
  • Добавить и сохранить
  • Поместить временную фиксацию

Я написал простую script для этой процедуры в ответе на другой вопрос, и есть шаги для выполнения процедуры в SourceTree здесь.

1

Когда вы пытаетесь переключаться между двумя ветвями, возникает такая ситуация.

Попробуйте добавить файлы, используя "git add filepath".

Далее выполните эту строку

git stash --keep-index

1

Я не знаю, как это сделать в командной строке, используя только SourceTree. Допустим, вы изменили файл A и имеете два смены в файле B. Если вы хотите сохранить только второй кусок в файле B и оставить все остальное нетронутым, сделайте следующее:

  • Сценическое все
  • Выполните изменения в вашей рабочей копии, которые отменят все изменения в файле A. (например, запустите инструмент внешнего разграничения и создайте файлы.)
  • Сделать файл B выглядеть так, как будто к нему применяется только второе изменение. (например, запустить внешний инструмент сравнения и отменить первое изменение.)
  • Создайте кошелек, используя "Keep staged changes".
  • Не все вещи
  • Готово!
1

Аналогичная ситуация. Сделал фиксацию и понял, что это не нормально.

git commit -a -m "message"
git log -p

Основываясь на ответах, это помогло мне.

# revert to previous state, keeping the files changed
git reset HEAD~
#make sure it ok
git diff
git status
#revert the file we don't want to be within the commit
git checkout specs/nagios/nagios.spec
#make sure it ok
git status
git diff
#now go ahead with commit
git commit -a -m "same|new message"
#eventually push tu remote
git push
0

Решение

Локальные изменения:

  • file_A (изменено) не поставлено
  • file_B (изменено) не поставлено
  • file_C (изменено) не поставлено

Чтобы создать stash "my_stash" только с изменениями file_C:

1. git add file_C
2. git stash save --keep-index temp_stash
3. git stash save my_stash
4. git stash pop stash@#{1}

Готово.


Объяснение

  • добавить file_C в промежуточную область
  • создайте кошелек с именем "temp_stash" и сохраните изменения в файле_C
  • создайте желаемый штамп ( "my_stash" ), только с изменениями в файле_c
  • примените изменения в "temp_stash" (file_A и file_C) к локальному коду и удалите stash

Вы можете использовать git статус между шагом, чтобы увидеть, что произошло.

0

Я не нашел ответа на то, что мне было нужно, и это так же просто:

git add -A
git reset HEAD fileThatYouWantToStash
git commit -m "committing all but one file"
git stash

Это фиксирует ровно один файл.

0

Одним сложным способом было бы сначала передать все:

git add -u
git commit // creates commit with sha-1 A

Reset вернитесь к исходной фиксации, но проверьте файл the_one_file из нового коммита:

git reset --hard HEAD^
git checkout A path/to/the_one_file

Теперь вы можете зашить the_one_file:

git stash

Очистка путем сохранения фиксированного содержимого в вашей файловой системе при возврате к исходному фиксации:

git reset --hard A
git reset --soft HEAD^

Да, несколько неудобно...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню