Видаліть конкретну комісію


287

Я працював з другом над проектом, і він редагував купу файлів, які не слід було редагувати. Якось я з’єднав його роботу зі своєю, або коли я її витягнув, або коли я намагався просто вибрати конкретні файли, які я хотів. Я довго дивився і граю, намагаючись зрозуміти, як видалити коміти, що містять правки до цих файлів, здається, що це перекидання між відновленням і перезавантаженням, і тут немає простих прикладів, Документи припускають, що я знаю більше, ніж я.

Отже, ось спрощена версія питання:

З огляду на наступний сценарій, як видалити комісію 2?

$ mkdir git_revert_test && cd git_revert_test

$ git init
Initialized empty Git repository in /Users/josh/deleteme/git_revert_test/.git/

$ echo "line 1" > myfile

$ git add -A

$ git commit -m "commit 1"
[master (root-commit) 8230fa3] commit 1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 myfile

$ echo "line 2" >> myfile

$ git commit -am "commit 2"
[master 342f9bb] commit 2
 1 files changed, 1 insertions(+), 0 deletions(-)

$ echo "line 3" >> myfile

$ git commit -am "commit 3"
[master 1bcb872] commit 3
 1 files changed, 1 insertions(+), 0 deletions(-)

Очікуваний результат

$ cat myfile
line 1
line 3

Ось приклад того, як я намагався відновити

$ git revert 342f9bb
Automatic revert failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>'
and commit the result.

5
Якщо хтось виявить це під час пошуку тієї самої проблеми, ось що я закінчив: Скопіюйте та вставте. Серйозно. Витративши 6+ годин, намагаючись змусити запропоновані рішення працювати, безрезультатно. Зрештою, я закінчився, витягнув оригінал і просто скопіював / вставив близько 20 файлів. Взяв менше 5 хвилин, і все було добре з тих пір (навіть коли ці файли об'єднуються із змінами в інших гілках, що відбувалися до цього фіаско). Я пропоную вам скористатися таким підходом. Мало того, що це найпростіше, я також підозрюю, що це єдине, що працює.
Джошуа Щока

Я зіткнувся з подібною проблемою, але, можливо, більш складною: мав відділення із сотнями комітетів, які я хотів розчарувати. На жаль, комісії з іншої гілки були об'єднані назад у гілку в проміжній точці, тому потрібен був "скинутий", перш ніж я міг скосити. Я пішов схожим шляхом, як запропонував tk нижче (вишня вибору + за допомогою позначення діапазону), але це призвело до конфліктів у деяких інших файлах. Зрештою, скопіювати та вставити + кілька ручних редагувань було найпростішим та передбачуваним шляхом вперед. Однозначно варто розглянути, якщо ви виявите, що витрачаєте на це занадто багато часу.
Федеріко

Відповіді:


73

Алгоритм, який використовує Git при обчисленні диференційованих даних для повернення, вимагає цього

  1. рядки, що повертаються, не змінюються жодними пізнішими комітами.
  2. щоб не було жодних інших "сусідніх" скоєнь пізніше в історії.

Визначення "суміжних" базується на кількості рядків за замовчуванням у контексті diff, що становить 3. Отже, якщо "myfile" було побудовано так:

$ cat >myfile <<EOF
line 1
junk
junk
junk
junk
line 2
junk
junk
junk
junk
line 3
EOF
$ git add myfile
$ git commit -m "initial check-in"
 1 files changed, 11 insertions(+), 0 deletions(-)
 create mode 100644 myfile

$ perl -p -i -e 's/line 2/this is the second line/;' myfile
$ git commit -am "changed line 2 to second line"
[master d6cbb19] changed line 2
 1 files changed, 1 insertions(+), 1 deletions(-)

$ perl -p -i -e 's/line 3/this is the third line/;' myfile
$ git commit -am "changed line 3 to third line"
[master dd054fe] changed line 3
 1 files changed, 1 insertions(+), 1 deletions(-)

$ git revert d6cbb19
Finished one revert.
[master 2db5c47] Revert "changed line 2"
 1 files changed, 1 insertions(+), 1 deletions(-)

Тоді все працює як очікувалося.

Друга відповідь була дуже цікава. Є функція, яка ще не була офіційно випущена (хоча вона доступна в Git v1.7.2-rc2) під назвою Revert Strategy. Ви можете викликати git так:

git revert - стратегія вирішення <commit>

і слід зробити кращу роботу, з'ясувавши, що ви мали на увазі. Я не знаю, що таке перелік доступних стратегій, а також не знаю визначення будь-якої стратегії.


1
perl -pкорисно для написання дуже коротких (один рядок) програм, які циклічно передають свій вхід на вихід, подібно до sed. perl -iвикористовується для редагування файлів на місці . perl -e- як подати код, що підлягає оцінці .
Хал Ейзен

281

Існує чотири способи:

  • Очистити шлях, повернути, але повернути в журнал відновлення:

    git revert --strategy resolve <commit>
    
  • Суворим способом видаліть лише останнє завдання:

    git reset --soft "HEAD^"
    

Примітка. Уникайте, git reset --hardоскільки це також відкине всі зміни у файлах з моменту останнього фіксації. Якщо --softне виходить, скоріше спробуйте --mixedабо --keep.

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

    git rebase -i HEAD~5
    

І якщо помилка допущена:

git rebase --abort
  • Швидке оновлення: видаліть лише певну комісію за допомогою свого id:

    git rebase --onto commit-id^ commit-id
    
  • Альтернативи: Ви також можете спробувати:

    git cherry-pick commit-id
    
  • Ще одна альтернатива:

    git revert --no-commit
    
  • В крайньому випадку, якщо вам потрібна повна свобода редагування історії (наприклад, через те, що git не дозволяє редагувати те, що ви хочете), ви можете використовувати цю дуже швидку програму з відкритим кодом: reposurgeon .

Примітка: звичайно, всі ці зміни проводяться локально, git pushпісля цього слід застосувати зміни до пульта. І якщо ваше репо не захоче видалити комітку ("заборонено перемотати вперед", що відбувається, коли ви хочете видалити комісію, яку ви вже натиснули), ви можете використовувати, git push -fщоб примусити натиснути зміни.

Примітка2: якщо ви працюєте на гілці і вам потрібно змусити натискати, вам слід абсолютно уникати, git push --forceоскільки це може перезаписати інші гілки (якщо ви внесли зміни в них, навіть якщо ваш поточний замовлення знаходиться на іншій гілці). Віддає перевагу завжди вказувати віддалений філія , коли ви змушуєте поштовх : git push --force origin your_branch.


На підставі пропозицій @gaborous: зробіть "git rebase -i HEAD ~ 2". Тепер у вас є кілька варіантів. У програмі vim можна побачити кілька коментованих рядків: один з них говорить вам про те, що ви можете просто видалити рядок (яка повинна бути зобов'язанням, від якого ви хочете позбутися), і ця комісія буде видалена разом із її журналом у вашій історії.
Ilker Cat

git revert - стратегія вирішення <commit>. Ця команда працювала для мене. дякую :)
Сватін

2
git rebase -i HEAD~5працював на мене. Тоді я просто зняв комісію, яка мені не потрібна, і я зміг вирішити проблему менше ніж за 30 секунд. Дякую.
lv10

118

Ось просте рішення:

git rebase -i HEAD~x

(Примітка: xкількість номерів)

після виконання блокнота файл відкриється. Введіть, dropкрім вашого зобов’язання.
Якщо ви не знаєте Vim, просто натисніть на кожен вибір слова, який ви хочете відредагувати, а потім натисніть клавішу "Я" (для режиму вставки). Після завершення введення натисніть клавішу "esc" для виходу з режиму вставки.



введіть тут опис зображення

і все, ви зробили ... Просто синхронізуйте приладну панель git і зміни будуть висунуті на віддалений.

Якщо комісія, яку ви скинули, вже була на пульті дистанційного керування, вам доведеться натиснути. Оскільки - сила вважається шкідливою , використовуйте git push --force-with-lease.


Чи є хороший спосіб дізнатись, яким має бути "х", якщо ви знаєте лише хеш-код цього питання?
jlewkovich

1
Для cpp-менталітету: x (кількість комісій) включно. наприклад, HEAD ~ 4 включає останні 4 коміти.
Herpes Free Engineer

ідеальна пропозиція. просто хочу додати, це також відкидає зміни від скинутого комітету.
celerno

намагався повернути 3 фіксацій: мерзотник перебазуватися -i HEAD-3 отримала помилку зі смертельними наслідками: Необхідно один переглядом недійсний вгору по течії «Голова 3»
Устин

1
@Ustin це ~ 3, а не -3
JD-V

37

Ваш вибір між

  1. збереження помилки та введення виправлення та
  2. видалення помилки та зміна історії.

Вам слід вибрати (1), якщо помилкову зміну виявив хтось інший, і (2), якщо помилка обмежена приватною невідштовхуваною гілкою.

Повернення Git - це автоматизований інструмент для виконання (1), він створює новий комітет, скасовуючи деякі попередні коміти. Ви побачите помилку та видалення в історії проекту, але люди, які витягнули з вашого сховища, не матимуть проблем під час оновлення. У вашому прикладі це не працює автоматизовано, тому вам потрібно відредагувати "мій файл" (щоб видалити рядок 2), зробити git add myfileта git commitвирішити конфлікт. Потім у вашій історії ви ввійдете з чотирма комісіями, виконуючи 4 повернення 2.

Якщо нікого не хвилює, що ваша історія зміниться, ви можете переписати її та видалити команду 2 (вибір 2). Найпростіший спосіб зробити це - використовувати git rebase -i 8230fa3. Це перетворить вас у редактор, і ви зможете вирішити не включати помилкову фіксацію, видаливши фіксацію (і продовжуючи "вибирати" поруч з іншими повідомленнями про виконання комісій. Читайте про наслідки цього .


База даних може бути складною, оскільки здається, що відбулося злиття.
Каскабель

3
git rebase -i 8230fa3, і видалити рядок фіксації відмінно працював для мене з моїми лише локальними змінами. Дякую!
Самуїл

26

Підхід 1

Спочатку дістаньте хеш комітів (наприклад: 1406cd61), який потрібно відновити. просте виправлення буде нижче команди,

$ git revert 1406cd61

якщо ви здійснили більше змін, пов’язаних з файлами 1406cd61 після фіксації 1406cd61, вище проста команда не буде працювати. Тоді вам доведеться зробити наступні кроки, а це збирання вишні.

Підхід 2

Будь ласка, дотримуйтесь нижче розгляду дій, оскільки ми використовуємо --force, для цього вам потрібно мати права адміністратора над git repo.

Крок 1: Знайдіть фіксацію перед комітом, який потрібно видалитиgit log

Крок 2: Оформити замовленняgit checkout <commit hash>

Крок 3: Створіть нову гілку, скориставшись поточним оформленням оформлення замовленняgit checkout -b <new branch>

Крок 4: Тепер вам потрібно додати фіксацію після видаленої фіксаціїgit cherry-pick <commit hash>

Крок 5: Тепер повторіть крок 4 для всіх інших зобов'язань, які ви хочете зберегти.

Крок 6: Після того, як усі комісії будуть додані до вашої нової гілки та були доручені. Перевірте, чи все в правильному стані та працює за призначенням. Перевірте все, що було зроблено:git status

Крок 7: Переключіться на вашу зламану гілкуgit checkout <broken branch>

Крок 8: Тепер виконайте жорсткий скидання на зламаній гілці до фіксації до того, який потрібно видалитиgit reset --hard <commit hash>

Крок 9: Об’єднайте свою фіксовану гілку в цю гілкуgit merge <branch name>

Крок 10: Відсуньте об'єднані зміни назад до походження. Попередження: Це замінить віддалене репо!git push --force origin <branch name>

Ви можете виконати процес без створення нової гілки, замінивши Крок 2 і 3 на Крок 8, потім не виконайте Крок 7 та 9.


1
Перший підхід спрацював як шарм. Він включає в себе коміт із зазначенням того, що бажане здійснення було скасовано, що дуже приємно для відстеження.
suarsenegger

18

Ви можете видалити небажані комісії за допомогою git rebase. Скажіть, ви включили деякі елементи з теми теми колеги у свою тематичну галузь, але пізніше вирішите, що ви не хочете цих комісій.

git checkout -b tmp-branch my-topic-branch  # Use a temporary branch to be safe.
git rebase -i master  # Interactively rebase against master branch.

У цей момент ваш текстовий редактор відкриє інтерактивний перегляд ребатів. Наприклад

git-rebase-todo

  1. Видаліть непотрібні комісії, видаливши їх рядки
  2. Збережіть і закрийте

Якщо база даних не вдалася, видаліть тимчасову гілку та спробуйте іншу стратегію. В іншому випадку продовжуйте виконувати наступні інструкції.

git checkout my-topic-branch
git reset --hard tmp-branch  # Overwrite your topic branch with the temp branch.
git branch -d tmp-branch  # Delete the temporary branch.

Якщо ви відсуваєте гілку теми на віддалений шлях, вам може знадобитися натиснути, оскільки історія фіксації змінилася. Якщо інші працюють на тій самій гілці, дайте їм голови вгору.


Чи можете ви надати приклад "спробувати іншу стратегію"?
pfabri

@pfabri, наприклад, ви можете вибрати два діапазони комітетів, коли ви не будете погані. Ви можете відмовитися від поганих зобов'язань. Ви можете уникнути використання рішення git, навіть скасовуючи зміни вручну, або починаючи зі свіжої гілки без поганої фіксації та вручну переробляючи хороші зміни. Якщо погана комісія містить конфіденційні дані, вам знадобиться більш ретельна стратегія: help.github.com/en/articles/…
Денніс

8

З інших відповідей тут я був git rebase -iдещо плутаний із тим, як можна було б скористатися для видалення комісії , тому сподіваюся, що це нормально, щоб записати тут мій тестовий випадок (дуже подібний до ОП).

Ось bashсценарій, який можна вставити, щоб створити тестовий сховище у /tmpпапці:

set -x

rm -rf /tmp/myrepo*
cd /tmp

mkdir myrepo_git
cd myrepo_git
git init
git config user.name me
git config user.email me@myself.com

mkdir folder
echo aaaa >> folder/file.txt
git add folder/file.txt
git commit -m "1st git commit"

echo bbbb >> folder/file.txt
git add folder/file.txt
git commit -m "2nd git commit"

echo cccc >> folder/file.txt
git add folder/file.txt
git commit -m "3rd git commit"

echo dddd >> folder/file.txt
git add folder/file.txt
git commit -m "4th git commit"

echo eeee >> folder/file.txt
git add folder/file.txt
git commit -m "5th git commit"

На даний момент у нас є такий file.txtвміст:

aaaa
bbbb
cccc
dddd
eeee

У цей момент HEAD знаходиться на 5-му фіксації, HEAD ~ 1 буде 4-м, а HEAD ~ 4 буде 1-м чином (тому HEAD ~ 5 не існував). Скажімо, ми хочемо видалити 3-й коміт - ми можемо випустити цю команду в myrepo_gitкаталог:

git rebase -i HEAD~4

( Зауважте, що git rebase -i HEAD~5результати з "фатальним: потрібна одноразова редакція; недійсна HEAD ~ 5". ) Текстовий редактор (див. Скріншот у відповіді @Dennis ) відкриється з цим вмістом:

pick 5978582 2nd git commit
pick 448c212 3rd git commit
pick b50213c 4th git commit
pick a9c8fa1 5th git commit

# Rebase b916e7f..a9c8fa1 onto b916e7f
# ...

Таким чином, ми отримуємо всі зобов’язання, оскільки (але не включаючи ) наш запитуваний HEAD ~ 4. Видаліть рядок pick 448c212 3rd git commitі збережіть файл; Ви отримаєте цю відповідь від git rebase:

error: could not apply b50213c... 4th git commit

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To check out the original branch and stop rebasing run "git rebase --abort".
Could not apply b50213c... 4th git commit

У цей момент відкрийте myrepo_git / folder/file.txtу текстовому редакторі; ви побачите, що він був змінений:

aaaa
bbbb
<<<<<<< HEAD
=======
cccc
dddd
>>>>>>> b50213c... 4th git commit

В основному, gitбачить, що коли HEAD дістався до другої комісії, був вміст aaaa+ bbbb; а потім у нього доданий патч cccc+, ddddякий він не знає, як додати до наявного вмісту.

Тому тут gitви не можете вирішити - саме ви повинні прийняти рішення: видаливши 3-й коміт, ви або зберігаєте внесені ним зміни (тут, рядок cccc) - або ви цього не робите. Якщо ви цього не зробите, просто видаліть зайві рядки, у тому числі cccc- у folder/file.txtтекстовому редакторі, так що це виглядає так:

aaaa
bbbb
dddd

... а потім збережіть folder/file.txt. Тепер ви можете задати наступні команди в myrepo_gitкаталозі:

$ nano folder/file.txt  # text editor - edit, save
$ git rebase --continue
folder/file.txt: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add

Ах - так, щоб відзначити , що ми вирішили конфлікт, ми повинні , перш ніж робити :git addfolder/file.txtgit rebase --continue

$ git add folder/file.txt
$ git rebase --continue

Тут текстовий редактор знову відкривається, показуючи рядок 4th git commit- тут ми маємо шанс змінити повідомлення про фіксацію (яке в цьому випадку може бути змістовно змінено на 4th (and removed 3rd) commitабо подібне). Скажімо, ви не хочете - тому просто вийдіть із текстового редактора без збереження; як тільки ви це зробите, ви отримаєте:

$ git rebase --continue
[detached HEAD b8275fc] 4th git commit
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/master.

На даний момент тепер у вас є така історія (яку ви також можете ознайомитись із gitk .програмою " Скажіть чи іншими інструментами") вмісту folder/file.txt(з, мабуть, незмінними часовими позначками оригінальних комісій):

1st git commit  |  +aaaa
----------------------------------------------
2nd git commit  |   aaaa
                |  +bbbb
----------------------------------------------
4th git commit  |   aaaa
                |   bbbb
                |  +dddd
----------------------------------------------
5th git commit  |   aaaa
                |   bbbb
                |   dddd
                |  +eeee

І якби раніше ми вирішили зберегти рядок cccc(вміст 3-го git-комітету, який ми видалили), у нас було б:

1st git commit  |  +aaaa
----------------------------------------------
2nd git commit  |   aaaa
                |  +bbbb
----------------------------------------------
4th git commit  |   aaaa
                |   bbbb
                |  +cccc
                |  +dddd
----------------------------------------------
5th git commit  |   aaaa
                |   bbbb
                |   cccc
                |   dddd
                |  +eeee

Що ж, саме таке читання я сподівався, що я знайду, щоб почати бачити, як git rebaseпрацює з точки зору видалення комітетів / змін; тож сподіваємось, що це може допомогти і іншим ...


Що безцінне проходження - точний варіант використання, з яким я боровся, це мені дуже допомогло.
pfabri

2

Таким чином, це здається, що погана комісія була включена в комісію злиття в якийсь момент. Ваша комісія з об'єднання вже витягнута? Якщо так, то ви хочете використовувати git revert; вам доведеться стиснути зуби і працювати над конфліктами. Якщо ні, то, можливо, ви можете або перезавантажити або відновити, але ви можете зробити це до здійснення об'єднання, а потім повторно об'єднати.

Насправді ми не можемо надати вам першої справи. Спробувавши відновити і виявивши, що автоматичний не вдався, вам доведеться вивчити конфлікти та виправити їх належним чином. Це точно той самий процес, що і виправлення конфліктів злиття; ви можете використовувати git statusдля того, щоб побачити, де конфлікти, редагувати безперервні файли, знаходити конфліктні файли, з'ясувати, як їх вирішити, додати конфліктні файли та, нарешті, здійснити. Якщо ви використовуєте git commitсамостійно (ні -m <message>), повідомлення, яке з'являється у вашому редакторі, має бути повідомленням шаблону, створеним компанією git revert; ви можете додати примітку про те, як виправили конфлікти, а потім збережіть і киньте вчиняти.

У другому випадку, виправляючи проблему перед об'єднанням, є два підкази, залежно від того, чи зробили ви більше роботи з моменту злиття. Якщо ви цього не зробили, ви можете просто git reset --hard HEAD^зняти злиття, виконати відновлення, а потім повторити злиття. Але я здогадуюсь, що у вас є. Отже, ви в кінцевому підсумку зробите щось подібне:

  • створити тимчасову гілку безпосередньо перед об'єднанням і перевірити її
  • виконайте повернення (або використовуйте git rebase -i <something before the bad commit> <temporary branch>для видалення поганих комісій)
  • повторити злиття
  • відновіть свою наступну роботу на: git rebase --onto <temporary branch> <old merge commit> <real branch>
  • видалити тимчасову гілку

1

Отже, ви зробили якусь роботу і підштовхнули її, давайте назвемо їх і A і B. Ваш колега також виконав певну роботу, і C і D. Ви об'єднали роботу своїх колег у свою (об'єднайте комісію E), потім продовжували працювати, дотримуючись цього, теж (вчинити F) і виявив, що ваш колега змінив деякі речі, які він не повинен мати.

Отже, ваша історія фіксацій виглядає приблизно так:

A -- B -- C -- D -- D' -- E -- F

Ви дуже хочете позбутися C, D і D '. Оскільки ви говорите, що ви об'єднали роботу своїх колег у свою, вони виконуються вже "там", тому видалення комірок за допомогою, наприклад, git rebase - ні-ні. Повірте, я спробував.

Тепер я бачу два виходи:

  • якщо ви ще не натискали E і F своєму колезі або комусь іншому (як правило, вашому "похідному" серверу), ви все одно можете видалити їх з історії. Це ваша робота, яку ви хочете зберегти. Це можна зробити за допомогою a

    git reset D'
    

    (замініть D 'фактичним хешем фіксації, який ви можете отримати з git log

    На цьому етапі комітети E і F перестали працювати, і зміни знову не передаються у вашому локальному робочому просторі. У цей момент я перемістив би їх у гілку або перетворив їх на патч і збереже для подальшого. Тепер відновіть роботу колеги автоматично git revertабо вручну. Коли ви зробите це, перезавантажте свою роботу поверх цього. У вас можуть виникнути конфлікти злиття, але принаймні вони будуть в коді, який ви написали, замість коду вашого колеги.

  • Якщо ви вже підштовхнули роботу, яку ви зробили після зобов'язань колеги, ви все одно можете спробувати отримати "зворотний патч" або вручну, або за допомогою git revert, але оскільки робота "в дорозі", так би мовити, ви, ймовірно, отримаєте більше конфліктів і збиття конфліктів. Схоже, саме так ви і закінчилися ...


0

git revert - стратегія вирішення, якщо фіксація є злиття: використовувати git revert --strategy resolution -m 1

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.