2013-12-02 3 views
41

У меня есть дерево git с множеством коммитов и множеством файлов. Теперь я хочу вернуть конкретные коммиты, которые касаются только файла. Для того, чтобы объяснить:Возвращение определенных коммитов из git

> git init 
Initialized empty Git repository in /home/psankar/specific/.git/ 
> echo "File a" > a 
> git add a ; git commit -m "File a" 
[master (root-commit) 5267c21] File a 
1 file changed, 1 insertion(+) 
create mode 100644 a 
> echo "File b" > b 
> git add b; git commit -m "File b" 
[master 7b560ae] File b 
1 file changed, 1 insertion(+) 
create mode 100644 b 
> echo "File c" > c 
> git add c; git commit -m "File c" 
[master fd6c132] File c 
1 file changed, 1 insertion(+) 
create mode 100644 c 
> echo "b and c modified" > b ; cp b c 
> git commit -a -m "b and c modified" 
[master 1d8b062] b and c modified 
2 files changed, 2 insertions(+), 2 deletions(-) 
> echo "a modified" > a 
> git commit -a -m "a modified" 
[master 5b7e0cd] a modified 
1 file changed, 1 insertion(+), 1 deletion(-) 
> echo "c modified" > c 
> git commit -a -m "c modified" 
[master b49eb8e] c modified 
1 file changed, 1 insertion(+), 1 deletion(-) 
> git log --pretty=oneline c 
> git log --pretty=oneline c | cat 
b49eb8e03af331bddf90342af7d076f831282bc9 c modified 
1d8b062748f23d5b75a77f120930af6610b8ff98 b and c modified 
fd6c13282ae887598d39bcd894c050878c53ccf1 File c 

Теперь я хочу вернуться только два коммиты b49eb8 и 1d8b06 без обращения изменений в а. IOW возвращает только коммиты в файле (не возвращая другие промежуточные коммиты (которые могут быть тысячи в числе) в разных файлах) Как это возможно?

+0

Использование 'git rebase -i ' – Cynede

+0

'git show --stat -p COMMITID' даст вам быструю статистику подсчета строк и патч, показывающий точные изменения, выполненные этим фиксатором. Вооружившись этой информацией, вы можете решить, хотите ли вы «git revert COMMITID». Использование '-n' позволит вам оценить до фиксации или даже' git revert -abort' – stackunderflow

ответ

66

Вы можете использовать git revert с опцией --no-commit. В вашем примере:

$ git revert --no-commit b49eb8e 1d8b062 
# Files that were modified in those 2 commits will be changed in your working directory 
# If any of those 2 commits had changed 'a' then you could discard the revert for it: 
$ git checkout a 
$ git commit -a -m "Revert commits b49eb8e and 1d8b062" 

Если вы не обеспечиваете коммита то Подготовленное сообщение будет доступно, когда коммит запускается редактор сообщений.

Если вы опустите --no-commit, тогда изменения в указанных вами коммандах будут отменены. Это достигается путем применения обратных изменений в указанных коммитах и ​​совершения этого. Это приведет к новой фиксации, и исходная, и возвращенная фиксация будут в истории вашего репозитория.

+4

+1 для переключателя '--no-commit' и упомянуть, что' revert' должен быть выполнен в ** обратном ** order – eckes

+1

Я пробовал то же самое в своей машине, прежде чем спрашивать здесь, и тогда это не сработало. Теперь попробовал новую настройку git (v 1.8), где она работает нормально. Вероятно, это ошибка в старой git, которая у меня была (1.6). Благодарю. –

11

Есть два случая здесь:

  1. Когда вы уже толкнул свой GIT дерево где-то и вы не хотите, чтобы изменить историю. В этом случае вам понадобится новый коммит, выражающий изменения, внесенные вами при возврате предыдущих коммитов. Вы должны использовать ответ @ mamapitufo.

  2. Если вы никогда не толкали ветвь, чтобы изменения были включены, и вы можете изменить историю. В этом случае вы можете полностью удалить нежелательные коммиты. Это будет способствовать истории и означает, что вы не подталкиваете неверного обращения к своим коллегам или общественности.

Во втором случае вы должны сделать git rebase -i. Найдите фиксацию, которая предшествует любой из истории, которую вы хотите изменить. Это может быть хэш фиксации или имя ветки или тега. Например, вы могли бы сделать

git rebase -i 23def8231 

или если вы начали с ветки origin/dev_branch и сделал работу, которая включает в себя биты для удаления на вашей ветке под названием dev_branch, вы могли бы сделать

git rebase -i origin/dev_branch 

Теперь вам будет отправлен в окно редактора, где вы увидите список всех коммитов, которые вы переустанавливаете. Это может быть vim - если вы обычно не редактируете в терминале, его можно установить как значение по умолчанию. Вам, вероятно, понадобится краткое руководство по vim и открытый ум, если это так.

Теперь проще всего удалить коммиты. Вы делаете это, удаляя строку или добавляя номер #, который указывает комментарий к началу строки. (В нем уже есть некоторые комментарии, чтобы объяснить вам все это. Игнорирование этих или их удаление не имеет эффекта.)

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

Важно помнить:

  • Если вы заблудились или удалить слишком много строк, вы можете отменить перебазироваться, удалив каждую фиксацию строки в файле, и сохранения. Процесс перезагрузки закончится тем, что ничего не изменится.
  • Возможно создание конфликтов. Например, если вы удаляете коммит, который редактирует файл, и оставляете в дальнейшем коммит, который редактирует одно и то же место. Позднее фиксация теперь будет неправильно применяться, и вам нужно будет вручную отредактировать или в инструменте слияния, чтобы получить нужную версию.

Вы также можете сделать много других манипуляций в git rebase -i. Например, изменение порядка коммитов, сложение нескольких вместе в один, добавление дополнительных изменений между фиксациями или изменение сообщений. Это очень полезно. Классический вариант использования - это очистка вашего местного отделения, прежде чем вы вернете его туда, где другие люди будут смотреть на ваши изменения.

Смежные вопросы