Как спрятать только те файлы, которые были добавлены?

Например, git status дает следующее:

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   app/src/[....]
    modified:   app/src/[....]
    new file:   app/src/[....]
    deleted:    app/src/[....]
    modified:   app/src/[....]
    modified:   test/unit/[....]
    modified:   test/unit/[....]
    new file:   test/unit/[....]
    deleted:    test/unit/[....]
    modified:   test/unit/[....]

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   test/unit/[....]

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    app/src/[....]/
    app/src/[....]/
    app/src/[....]/

(я удалил имена файлов)

Как мне спрятать только те изменения, которые у меня есть git added (т. е. «Изменения, которые нужно зафиксировать», а не неустановленные изменения или неотслеживаемые файлы), чтобы вместо этого я мог перенести их в другую ветку?


person peter-b    schedule 20.05.2015    source источник
comment
Действительно, но как я могу игнорировать отслеживаемые, но не добавленные файлы, подобные test/unit/[....] выше?   -  person peter-b    schedule 20.05.2015
comment
возможный дубликат Сохранение только поэтапных изменений в git - возможно ли это?   -  person isherwood    schedule 20.05.2015
comment
@peter-b, вы хотите только спрятать индекс?   -  person Chris    schedule 20.05.2015
comment
@ Крис, это правильно.   -  person peter-b    schedule 20.05.2015


Ответы (3)


Основным механизмом хранения Git является фиксация — на самом деле все, что git stash делает, — это делает пару несколько необычных коммитов, поэтому ответ Ника Волынкина является правильным. Однако, возможно, потребуется немного расширения, и есть более простые (ну, потенциально более простые) методы.

Я не очень большой поклонник git stash, но если вы привыкли его использовать, вот самый простой из других методов:

  1. git stash save (он же git stash). Это записывает две фиксации, одну на основе текущего индекса, а вторую для хранения еще не подготовленных файлов рабочего дерева. (Если вам нужно сохранить неотслеживаемые файлы, вы можете добавить флаг -u, а затем скрипт stash добавит еще одну третью фиксацию. Обычно вы можете просто оставить эти неотслеживаемые файлы в вашем рабочем дереве неотслеживаемыми, хотя.) Эти коммиты не находятся ни в одной ветке, они находятся только в/в специальном тайнике ref. Между тем, вы все еще находитесь не в той ветке, которую я ниже назову wrongbr.

  2. git checkout ветка, в которой вы хотите их разместить. Теперь вы находитесь на правильной ветке.

  3. git stash apply --index. При этом используются специальные коммиты тайника, сделанные на шаге 1, но при этом они остаются там (apply) в тайнике. --index очень важен: он указывает сценарию хранения разделять файлы индекса и неустановленные файлы, т. е. возвращает вам предварительно настроенные и неустановленные файлы.

    Если все пойдет хорошо, теперь вы можете git commit внести изменения в нужную ветку. Предыдущие файлы снова помещаются в промежуточные файлы, а неподготовленные файлы по-прежнему остаются неподготовленными, потому что вы apply отредактировали тайник с помощью --index. Коммит зафиксирует проиндексированные файлы, оставив непроиндексированные файлы непроиндексированными.

  4. Теперь вы можете вернуться к другой неправильной ветке (там, где вы изначально создали тайник) и git stash apply или git stash pop с --index или без него. Возможно, вам придется очистить все неустановленные файлы (и это безопасно, они все еще находятся в тайнике): git reset --hard, затем git checkout wrongbr, затем git stash pop. Обратите внимание, что pop — это просто apply, за которым следует drop: мы не хотели удалять тайник на шаге 3 (в тайнике есть единственная копия исходного модифицированного, но- неустановленные файлы), поэтому мы использовали там apply, но теперь мы (предположительно) действительно хотим удалить тайник, поэтому использование pop допустимо.

Однако на шаге 3 есть большая потенциальная ловушка: возможно, тайник не будет применяться правильно. Если это так, вы должны использовать какой-либо другой метод. Это одна из причин, по которой мне не очень нравится система stash: она не работает в сложных случаях и оставляет вам необходимость знать инструменты, которые вы можете использовать, если не используете stash. В этом случае вы можете просто использовать эти инструменты большую часть времени... а затем использовать stash как удобный ярлык для случаев, когда вы уверены, что он будет работать.


Предыстория: фиксация берет все, что сейчас есть в индексе — git ls-files --cached покажет полное содержимое, а git status обрезает это до интересного и добавляет дополнительную полезную информацию — и делает из них фиксацию со всеми необходимыми объектами дерева и так далее. на. Родительский коммит нового коммита — это то, чем был предыдущий коммит.

Вы хотите, чтобы ваша новая фиксация была сделана в другой ветке. Один из способов сделать это — сделать это сейчас, в текущей ветке; затем скопируйте эту фиксацию в новую, другую фиксацию в другой ветке. Чтобы сделать фиксацию копии для новой, другой фиксации в другой ветке, вы можете использовать git cherry-pick. Это правда, что вы можете использовать git rebase: под одеялом он использует сам git cherry-pick. Но rebase не совсем подходящий инструмент: он предназначен для массового сбора вишен, и у вас есть только один коммит; и в конце он использует git reset для перемещения метки ветки, но не совсем так, как вы хотите. Вы можете заставить его работать, но есть несколько более подходящих инструментов.

Однако вернемся к исходной проблеме: вы хотите взять текущий индекс и использовать его для создания новой фиксации, но в другой ветке. Это было бы проще всего, если бы вы могли просто переключиться на другую ветку сейчас, ничего не делая, а затем сделать новую фиксацию.

Скорее всего, вы можете сделать это. Просто git checkout otherbranch, а затем git commit. Здесь возможны три случая:

  1. Другой ветки пока не существует. Большой! Используйте git checkout -b newbranch, чтобы создать его, начиная с того места, где вы сейчас находитесь. Затем git commit. Все готово, если только вы не хотите перебазировать новую ветку, чтобы она начиналась не с того места, где вы сейчас находитесь. Если это так, используйте git rebase в новой ветке. Обратите внимание, что вы можете сделать это перебазирование позже, после того, как позаботитесь о неустановленных файлах.

  2. Другая ветвь существует, и — вам повезло — git checkout otherbranch работает нормально. Сделайте это и зафиксируйте, и все готово. Затем вы можете git checkout выбрать любую ветку для неустановленных файлов.

  3. Самый неприятный случай: другая ветка существует, но git checkout говорит вам, что вы перезапишете то, что не зафиксировали.

Случай 3 — это тот, где вам нужно совершить или спрятать.

Что делать здесь, зависит от того, что вам наиболее удобно. Вы можете, например, попробовать четырехшаговый метод stash, описанный выше, как самую простую альтернативу.

Однако для себя я бы просто зафиксировал сейчас, не в той ветке, а затем снова зафиксировал (или использовал git stash), чтобы убрать неустановленные файлы. Это дает мне коммит, который я могу git cherry-pick переместить в правую ветвь. Вот пример последовательности, которая, вероятно, сработает:

  1. git commit, чтобы сделать фиксацию, но не в той ветке (давайте назовем вашу текущую ветку wrongbr для справки ниже).
  2. git stash save, чтобы сохранить неустановленные изменения (или, с -u, также неотслеживаемые файлы).
  3. git checkout ветка, в которой вы хотите, чтобы коммит был, например, git checkout rightbr.
  4. git cherry-pick wrongbr. Если это удастся, хорошо; если нет, отредактируйте файлы по мере необходимости, чтобы очистить после слияния проблемы, затем git commit результат.
  5. git checkout wrongbr: теперь мы исправим это, удалив коммит, скопированный на шаге 4.
  6. git reset --hard HEAD^: удаляет скопированный нами коммит.
  7. git stash pop (или git stash apply && git stash drop, который делает то же самое, вариант apply просто дает вам возможность проверить результат перед тем, как вы drop заберете тайник).

Обратите внимание на шаг 4: git cherry-pick берет именованный коммит (наконечник wrongbr, который содержит нужный нам коммит, который просто находится не в той ветке), сравнивает его с его родителем, а затем пытается применить полученный diff в текущую ветку. Возможно, потребуется выполнить трехстороннее слияние, если файлы в текущей ветке сильно отличаются от соответствующих им файлов в wrongbr. Это то же самое место, где возникают сложности в простом случае простого извлечения rightbr и первоначальной фиксации. То есть мы делаем эту длинную версию, потому что самый неприятный случай произошел, когда мы пытались просто git checkout rightbr перед фиксацией, поэтому есть хороший шанс, что нам нужно что-то исправить. Это также может вызвать проблемы с исходным 4-шаговым методом stash.

person torek    schedule 20.05.2015
comment
Проголосовал за гораздо лучший объясняющий и полный ответ. - person Nick Volynkin; 21.05.2015
comment
Это должен быть один из тех ответов с непристойным количеством голосов. Я не знаю, почему это не так. - person Michael Dorst; 01.03.2020

Зафиксируйте эти файлы, а затем перебазируйте фиксацию.

person Nick Volynkin    schedule 20.05.2015

  1. Спрячьте все, но сохраните индекс (в этом тайнике будут поэтапные (от git add) и неэтапные правки):

    git stash --keep-index

  2. Спрячьте индекс, это все, что осталось после тайника на шаге 1 (это то, что вы хотели спрятать).

    git stash

  3. Вскройте или примените (безопаснее) тайник, в котором было все это, тот, что был на шаге 1 (теперь в stash@{1}, потому что самый новый всегда stash@{0}).

    git stash apply stash@{1}

  4. Восстановите файлы, которые были изначально добавлены в индекс, те, что в stash@{0}, извлекая их из HEAD

    git checkout HEAD ./path/to/files/to/reset ./another/path/to/other/file/to/reset

  5. Если вы ошиблись на шаге 4, попробуйте git reset HEAD --hard стереть все локальные изменения и начать заново с шага 3.

person kilogic    schedule 26.07.2017