Как сбросить git --жесткий подкаталог?
обновление: это будет работать более интуитивно, как в Git 1.8.3, см. мой собственный ответ.
представьте себе следующий вариант использования: я хочу избавиться от всех изменений в определенном подкаталоге моего рабочего дерева Git, оставив все остальные подкаталоги нетронутыми.
Я могу сделать
git checkout .
, но git checkout . добавляет каталоги, исключенные из sparse checkout-
здесь
git reset --hard
, но это не позвольте мне сделать это для подкаталога:> git reset --hard . fatal: Cannot do hard reset with paths.
опять же: почему git не может выполнять жесткие/мягкие сбросы по пути?
Я могу исправить текущее состояние с помощью
git diff subdir | patch -p1 -R
, но это довольно странный способ делать это.
какова правильная команда Git для этой операции?
скрипт, приведенный ниже, иллюстрирует эту проблему. Вставьте соответствующую команду под How to make files
комментарий -- текущая команда восстановит файл a/c/ac
, который должен быть исключен неполное извлечение. Обратите внимание, что я не хотите явно восстановить a/a
и a/b
, Я только "знаю" a
и хотите восстановить все ниже. EDIT: и я тоже не "знаю"b
, или какие другие каталоги находятся на том же уровне, что и a
.
#!/bin/sh
rm -rf repo; git init repo; cd repo
for f in a b; do
for g in a b c; do
mkdir -p $f/$g
touch $f/$g/$f$g
git add $f/$g
git commit -m "added $f/$g"
done
done
git config core.sparsecheckout true
echo a/a > .git/info/sparse-checkout
echo a/b >> .git/info/sparse-checkout
echo b/a >> .git/info/sparse-checkout
git read-tree -m -u HEAD
echo "After read-tree:"
find * -type f
rm a/a/aa
rm a/b/ab
echo >> b/a/ba
echo "After modifying:"
find * -type f
git status
# How to make files a/* reappear without changing b and without recreating a/c?
git checkout -- a
echo "After checkout:"
git status
find * -type f
8 ответов:
Примечание (как прокомментировал by Dan Fabulich), что:
git checkout -- <path>
не выполняет жесткий сброс: он заменяет содержимое рабочего дерева на поэтапное содержимое.git checkout HEAD -- <path>
выполняет жесткий сброс для пути, заменяя как индекс, так и рабочее дерево версией изHEAD
совершал.как ответил by Ajedi32, обе формы оформления заказа не удалять файлы которые были удалены в целевой редакции.
Если у вас есть дополнительные файлы в рабочем дереве, которые не существуют в голове, agit checkout HEAD -- <path>
не будет удалять их.но эта проверка может уважать a
git update-index --skip-worktree
(для тех каталогов, которые вы хотите игнорировать), как указано в "почему исключенные файлы продолжают появляться в моем git sparse checkout?".
По словам разработчика Git Duy Nguyen, который любезно реализовал функция и переключатель совместимости, следующие работы, как ожидалось начиная с Git 1.8.3:
git checkout -- a
(где
a
это каталог, который вы хотите жестко сбросить). К исходному поведению можно получить доступ черезgit checkout --ignore-skip-worktree-bits -- a
попробуйте изменить
git checkout -- a
до
git checkout -- `git ls-files -m -- a`
начиная с версии 1.7.0, в Git
ls-files
чествует флаг skip-worktree.запуск тестового сценария (с некоторыми незначительными изменениями настроек
git commit
... кgit commit -q
иgit status
доgit status --short
) выходы:Initialized empty Git repository in /home/user/repo/.git/ After read-tree: a/a/aa a/b/ab b/a/ba After modifying: b/a/ba D a/a/aa D a/b/ab M b/a/ba After checkout: M b/a/ba a/a/aa a/c/ac a/b/ab b/a/ba
запуск тестового сценария с предлагаемым
checkout
изменить выходы:Initialized empty Git repository in /home/user/repo/.git/ After read-tree: a/a/aa a/b/ab b/a/ba After modifying: b/a/ba D a/a/aa D a/b/ab M b/a/ba After checkout: M b/a/ba a/a/aa a/b/ab b/a/ba
в случае просто отбрасывания изменений,
git checkout -- path/
илиgit checkout HEAD -- path/
команды, предложенные другими ответами, отлично работают. Однако, когда вы хотите сбросить каталог в ревизию, отличную от HEAD, это решение имеет значительную проблему: оно не удаляет файлы, которые были удалены в целевой ревизии.поэтому вместо этого я начал использовать следующую команду:
это работает, находя разницу между целевой фиксацией и индексом, а затем применяя эту разницу в обратном направлении к рабочему каталогу и индексу. В принципе, это означает, что содержимое индекса соответствует содержимому указанной вами ревизии. Дело в том, что
git diff
принимает аргумент path позволяет ограничить этот эффект определенным файлом или каталогом.так как это команда довольно длинная, и я планирую использовать ее часто, я установил для нее псевдоним, который я назвал
reset-checkout
:git config --global alias.reset-checkout '!f() { git diff --cached "$@" | git apply -R --index; }; f'
вы можете использовать его как это:
git reset-checkout 451a9a4 -- path/to/directory
или так:
git reset-checkout 451a9a4
сброс обычно меняет все, но вы можете использовать
git stash
, чтобы забрать то, что вы хотите сохранить. Как вы упомянули,stash
не принимает путь напрямую, но он все еще может быть использован для сохранения определенного пути с--keep-index
флаг. В вашем примере вы бы спрятали каталог b, а затем сбросили все остальное.# How to make files a/* reappear without changing b and without recreating a/c? git add b #add the directory you want to keep git stash --keep-index #stash anything that isn't added git reset #unstage the b directory git stash drop #clean up the stash (optional)
это приведет вас к точке, где последняя часть вашего скрипта будет выводить это:
After checkout: # On branch master # Changes not staged for commit: # # modified: b/a/ba # no changes added to commit (use "git add" and/or "git commit -a") a/a/aa a/b/ab b/a/ba
Я считаю, что это была цель результат (b остается измененным, a/* файлы возвращаются, a/c не воссоздается).
этот подход имеет дополнительное преимущество быть очень гибким; вы можете получить как мелкозернистый, как вы хотите, добавляя определенные файлы, но не другие, в каталоге.
Если размер подкаталога не особенно огромен, и вы хотите держаться подальше от CLI, вот быстрое решение вручную сброс подкаталог:
- переключиться на ветку master и скопировать в поддиректорию, чтобы быть сброшены.
- теперь вернитесь к своей ветке функций и замените подкаталог на копию, которую вы только что создали на шаге 1.
- сохранить изменения.
Ура. Вы просто вручную сбросите подкаталог в вашей ветке функций должен быть таким же, как и в главной ветке !!
Ajedi32 ' s ответ это то, что я искал, но для некоторых коммитов я столкнулся с этой ошибкой:
error: cannot apply binary patch to 'path/to/directory' without full index line
может быть потому, что некоторые файлы каталога являются двоичными файлами. Добавление опции' --binary ' в команду git diff исправлено:
git diff --binary --cached commit -- path/to/directory | git apply -R --index
Я собираюсь предложить ужасный вариант здесь, так как я понятия не имею, как делать что-либо с git, кроме
add
commit
иpush
, вот как я "вернул" подкаталог:Я запустил новый репозиторий на своем локальном ПК, вернул все это к фиксации, из которой я хотел скопировать код, а затем скопировал эти файлы в свой рабочий каталог,
add
commit
push
и вуаля. Не ненавидьте игрока, ненавидьте Мистера Торвальдса за то, что он умнее всех нас.