Вопрос: Вырезать только один файл из нескольких файлов, которые были изменены с Git?


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


2392


источник


Ответы:


Предупреждение

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


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

git stash --keep-index

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

  1. git rebase -i <last good commit>
  2. Отметьте некоторые изменения как edit,
  3. git reset HEAD^
  4. git add <files you want to keep in this change>
  5. git stash --keep-index
  6. При необходимости исправьте вещи. Не забывайте git addлюбые изменения.
  7. git commit
  8. git stash pop
  9. Повторите, начиная с № 5, при необходимости.
  10. git rebase --continue

1212



Вы также можете использовать git stash save -p "my commit message", Таким образом, вы можете выбрать, какие блоки должны быть добавлены в 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

2558



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

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

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

Видеть " Изменяет привязку к определенным файлам " для большего.


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

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

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

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

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

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


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

Все же, git stash save --patchможет позволить вам добиться частичного запирания, которое вы после:

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

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

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

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


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

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

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

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


223



When git stash -p (or git add -p with stash --keep-index) would be too cumbersome, I found it easier to use diff, checkout and apply:

To "stash" a particular file/dir only:

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

Then afterwards

git apply stashed.diff

76



Let's say you have 3 files

a.rb
b.rb
c.rb

and you want to stash only b.rb and c.rb but not a.rb

you can do something like this

# 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

And you are done! HTH.


43



Use git stash push, like this:

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

For example:

git stash push -- my/file.sh

This is available since Git 2.13, released in spring 2017.


28



Another way to do this:

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

I came up with this after I (once again) came to this page and didn't like the first two answers (the first answer just doesn't answer the question and I didn't quite like working with the -p interactive mode).

The idea is the same as what @VonC suggested using files outside the repository, you save the changes you want somewhere, remove the changes you don't want in your stash, and then re-apply the changes you moved out of the way. However, I used the git stash as the "somewhere" (and as a result, there's one extra step at the end: removing the cahnges you put in the stash, because you moved these out of the way as well).


25



Update (2/14/2015) - I've rewritten the script a bit, to better handle the case of conflicts, which should now be presented as unmerged conflicts rather than .rej files.


I often find it more intuitive to do the inverse of @bukzor's approach. That is, to stage some changes, and then stash only those staged changes.

Unfortunately, git doesn't offer a git stash --only-index or similar, so I whipped up a script to do this.

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

You can save the above script as git-stash-index somewhere on your path, and can then invoke it as git stash-index

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

Now the stash contains a new entry that only contains the changes you had staged, and your working tree still contains any unstaged changes.

In some cases, the working tree changes may depend on the index changes, so when you stash the index changes, the working tree changes have a conflict. In this case, you'll get the usual unmerged conflicts that you can resolve with git merge/git mergetool/etc.


22



Since creating branches in Git is trivial you could just create a temporary branch and check the individual files into it.


16



Just in case you actually mean 'discard changes' whenever you use 'git stash' (and don't really use git stash to stash it temporarily), in that case you can use

git checkout -- <file>

Note that git stash is just a quicker and simple alternative to branching and doing stuff.


11



Save the following code to a file, for example, named stash. Usage is stash <filename_regex>. The argument is the regular expression for the full path of the file. For example, to stash a/b/c.txt, stash a/b/c.txt or stash .*/c.txt, etc.

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

Code to copy into the file:

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



The problem with VonC's `intermediate' solution of copying files to outside the Git repo is that you lose path information, which makes copying a bunch of files back later on somewhat of a hassle.

A find it easier to use tar (similar tools will probably do) instead of copy:

  • tar cvf /tmp/stash.tar path/to/some/file path/to/some/other/file (... etc.)
  • git checkout path/to/some/file path/to/some/other/file
  • git stash
  • tar xvf /tmp/stash.tar
  • etc. (see VonC's `intermediate' suggestion)

7