Не могу удалить коммит от Мастера GIT - PullRequest
0 голосов
/ 07 марта 2020

Я не могу удалить коммит из мастера. Я перепробовал множество команд из stackOverflow, но ничего не работает.

Последняя команда git revert 655734672ea28811e0723981853f05cbcdda8ec8 только что изменила коммит. Вот так это выглядит сейчас.

.............................................(master)
$ git log
commit ddd171a390ce613a87e7f4e00135b48a1b03c2ad (HEAD -> master)
Author: pet......... <fffffafafa@yahoo.com>
Date:   Sat Mar 7 15:08:57 2020 +0000

    Revert "adding jacoco-maven-plugin"
    This reverts commit 655734672ea28811e0723981853f05cbcdda8ec8.`
commit 655734672ea28811e0723981853f05cbcdda8ec8 (_D)
Author: pet.... <petttttttt@yahoo.com>
Date:   Sat Mar 7 13:42:48 2020 +0000

   adding jacoco-maven-plugin

Ответы [ 2 ]

1 голос
/ 08 марта 2020

Git:

  1. распределен, то есть существует более одного репозитория; и
  2. специально созданы для удаления коммит немного труден: ему «нравится» добавлять коммитов, а не удалять их.

Ваш первоначальный вопрос не учитывает тот факт, что, добавив два коммита, вы опубликовал их на GitHub. (Вы, в конце концов, отметили это в комментарии .)

Если вы действительно хотите удалить некоторых коммитов, теперь вы должны убеждать каждые Git у них есть , чтобы бросить их. Даже если / когда вам это удастся, они не исчезнут сразу: в общем, у вас есть как минимум 30 дней, чтобы передумать и вернуть их снова. Но мы не будем вдаваться в это здесь. Давайте просто посмотрим, как вы убедите один Git в отбросить некоторые коммиты, используя git reset.

Мы будем использовать git reset --hard здесь по причинам, в которые мы не будем входить должным образом. Имейте в виду, что git reset --hard уничтожает незафиксированные работы, поэтому, прежде чем сделать это, убедитесь, что у вас нет ничего незафиксированного, что вы хотите сохранить.

Как прокомментировал Тарек Дахран , вы, вероятно, хотели git reset --hard HEAD^^. Однако вам понадобится нечто большее - и прежде чем вслепую любую из этих команд, очень важно 1045 * понять две вещи:

  • что собирается делать этот тип git reset;
  • что означает HEAD^^.

Чтобы попасть туда и посмотреть, как выполнить сброс вашего GitHub Кроме того, давайте сделаем краткий обзор некоторых Git основ.

Git хранит коммиты

Git это все о коммитах . Git на самом деле не о филиалах и не о файлах . Это около коммитов . Коммит - это ваша основная единица хранения в Git. 1 Содержимое коммита состоит из двух частей.

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

Между тем, остальные коммит состоит из метаданных или информации о коммите: кто его сделал, когда и так далее. Эти метаданные связывают каждый новый коммит с некоторым существующим коммитом. Как и моментальный снимок, эти метаданные останавливаются навсегда, как только вы делаете коммит. Никто и ничто не может изменить это: не вы, и даже не Git сам.

Неизменяемость каждой части коммита - это то, что делает коммиты великолепными для архивирования ... и совершенно бесполезен для выполнения любой новой работы. Вот почему Git обычно извлекает один коммит, над которым вы можете работать. Сжатые, обезвоженные и замороженные на все время файлы получаются, размораживаются, повторно увлажняются и превращаются в обычные файлы. Эти файлы вообще не в Git, но они позволяют вам выполнить вашу работу. Это часть того, почему сброс --hard относительно опасен (но не вся история). Здесь мы проигнорируем эти используемые файлы и сконцентрируемся на самих коммитах.

Каждый коммит имеет уникальный га sh ID . Ха sh ID коммита в некотором смысле является коммитом. Этот идентификатор ha sh зарезервирован для , который commit, и только , который когда-либо может использовать его. В некотором смысле, этот идентификатор sh был зарезервирован для этого коммита до того, как вы сделали коммит, и все еще зарезервирован для этого коммита, даже после того, как вам удастся удалить его. Поскольку идентификатор имеет , чтобы быть уникальным - и зарезервирован на все время для , который фиксирует - это должно быть действительно огромное число. Вот почему идентификаторы коммитов ha sh такие большие и некрасивые, как 51ebf55b9309824346a6589c9f3b130c6f371b8f (и даже они уже недостаточно велики - они насчитывают только около 10 48 - и Git движется к более крупным).

Уникальность этих идентификаторов ha sh означает, что каждый Git, имеющий клон какого-либо хранилища, может общаться с любым другим Git, который имеет клон того же хранилища, и просто обмениваются га sh идентификаторами с другим Git. Если у вашего Git есть 51ebf55b9309824346a6589c9f3b130c6f371b8f, а у их Git нет, то у вас есть коммит, которого нет, и они могут получить его от вас. Теперь у вас обоих есть 51ebf55b9309824346a6589c9f3b130c6f371b8f.

Это было бы все, что нужно, кроме той части, где мы говорили, что каждый коммит может хранить ha sh ID (ы) некоторых более ранних коммитов ). Коммит 51ebf55b9309824346a6589c9f3b130c6f371b8f хранит коммит га sh ID f97741f6e9c46a75b4322760d77322e53c4322d7. Это его родитель: коммит, который предшествует ему.


1 Коммит может быть разбит на более мелкие единицы: фиксировать объекты, деревья и поддеревья, а также объекты BLOB-объектов. Но это не тот уровень, на котором вы обычно имеете дело с Git.


Это ключ к Git и ответвлениям

Теперь мы видим, что:

  • Каждый коммит имеет большой уродливый идентификатор sh, уникальный для коммита .
  • Каждый коммит содержит идентификатор sh * некоторого ранее commit, который является родительским этого коммита. (Коммит может содержать два родительских идентификатора ha sh, что делает коммит коммитом слияния , но мы не будем вдаваться в них здесь.)

Когда что-либо содержит commit ha sh ID, мы говорим, что вещь указывает на commit. Таким образом, коммиты образуют цепочки, указывающие назад.

Если мы используем одинарные заглавные буквы для обозначения идентификаторов ha sh, мы можем нарисовать такую ​​цепочку коммитов, как эта. Представьте, что у нас есть почти новый репозиторий с тремя коммитами. Мы назовем их A, B и C, хотя в действительности у них есть несколько случайных идентификаторов ha sh. Коммит C - это последний коммит, поэтому он указывает на B:

    B <-C

Но B имеет стрелку, указывающую назад (на самом деле, сохраненный ха sh ID) указывает на A:

A <-B <-C

Фиксация A является особенной. Будучи самым первым коммитом, когда-либо сделанным в этом репозитории, он не может указать назад, поэтому он этого не делает.

Добавление новых коммитов в репозиторий просто состоит из выписывания нового коммита так что он указывает на предыдущий последний коммит. Например, чтобы добавить новый коммит, который мы назовем D, мы просто нарисуем его:

A <-B <-C <-D

Никакая часть какого-либо существующего коммита не может измениться, но ни одна не делает: C не указывает на D, D указывает на C. Так что у нас все хорошо, кроме одного. Эти идентификаторы ha sh в реальном хранилище не просто увеличиваются таким образом. У нас есть большие уродливые вещи, такие как 51ebf55b9309824346a6589c9f3b130c6f371b8f и f97741f6e9c46a75b4322760d77322e53c4322d7. Там нет очевидного способа привести их в порядок. Как мы узнаем, какой из них последний?

Учитывая любой идентификатор ha sh, мы можем использовать этот коммит, чтобы найти предыдущий коммит. Таким образом, один из вариантов: извлечь каждый коммит в хранилище и посмотреть, какие из них не имеют ничего, указывающего на них. Сначала мы можем извлечь C, затем A, затем B, затем D или какой-то другой порядок, а затем мы рассмотрим все из них и поймем, что, эй, D указывает на C указывает на B указывает на A, но никто не указывает на D, поэтому он должен быть последним.

Это работает, но очень медленно в большом хранилище , Проверка всего этого может занять несколько минут.

Мы могли бы записать га sh идентификатор последнего коммита (в настоящее время D), возможно, на клочке бумаги или доска. Но у нас есть компьютер! Почему бы не записать это в компьютер? И вот что делает название ветви: это просто файл, или строка в файле, или что-то в этом роде, 2 , где Git набросал га sh идентификатор последнего коммита в цепочке. Так как у него есть * sh ID коммита, указывает на коммит:

A--B--C--D   <-- master

Теперь мы можем стать ленивыми и перестать рисовать стрелки между коммиты, так как они никогда не могут измениться, но давайте продолжим рисовать их из имен ветвей, потому что они делают изменения Чтобы добавить новый коммит к master, мы делаем новый коммит. Он получает случайный идентификатор ha sh, который мы назовем E, и указывает на D. Тогда у нас будет Git запись ха sh ID нового коммита E в имя master, так что master указывает на E сейчас:

A--B--C--D--E   <-- master

Давайте создайте новое имя филиала , feature, сейчас. Это также указывает на существующий коммит E:

A--B--C--D--E   <-- feature, master

Теперь давайте создадим новый коммит F. Какое имя ветки обновляется? Чтобы ответить на последний вопрос, Git использует специальное имя HEAD. Он сохраняет имя ветви в файле HEAD. 3 Мы можем нарисовать это, прикрепив специальное имя HEAD к одной из двух ветвей:

A--B--C--D--E   <-- feature, master (HEAD)

Здесь мы используем commit E , потому что мы on branch master, как сказал бы git status. Если мы сейчас git checkout feature, мы продолжим , используя коммит E, но теперь мы on branch feature, например:

A--B--C--D--E   <-- feature (HEAD), master

Теперь давайте создадим новый коммит F , Git заставит F указать обратно на E, текущий коммит, и затем сделает текущее имя ветви , указывающее на новый коммит F:

A--B--C--D--E   <-- master
             \
              F   <-- feature (HEAD)

Мы только что увидели, как растут ветки в одном репозитории.


2 Git в настоящее время использует оба метода для хранения информации о ветвях на ha sh -ID. Информация может находиться в своем собственном файле или может быть строкой в ​​каком-либо общем файле.

3 Этот HEAD файл в настоящее время всегда является файлом. Это очень особенный файл: если ваш компьютер выходит из строя и восстанавливается, он удаляет файл HEAD, Git перестанет считать, что ваш репозиторий является репозиторием. Поскольку файл HEAD имеет тенденцию быть довольно активным, это на самом деле немного распространено - не то, что компьютер часто выходит из строя, а в том, что когда это происходит, вам нужно заново создать файл, чтобы сделать функцию хранилища.


Git распространяется

Когда мы используем Git, у нас вряд ли когда-либо будет просто один репозиторий. В вашем случае, например, у вас есть как минимум два репозитория:

  • ваш, на вашем ноутбуке (или любом другом компьютере); и
  • ваш репозиторий GitHub, хранящийся на компьютерах GitHub (эта часть «облака»).

Каждый из этих репозиториев имеет свои собственные имена ветвей , Вот что может вызвать все наши проблемы.

Чтобы создать хранилище для вашего ноутбука, вы, вероятно, запустили:

git clone <url>

Это создаст новый пустой репозиторий на вашем ноутбуке. В этом пустом хранилище он создает remote , используя имя origin. Пульт ДУ хранит URL-адрес, который вы указали в командной строке (или вписал в то, что вы использовали GUI), так что теперь вместо ввода https://github.com/... вы можете просто ввести origin, что проще и короче. Затем ваш Git вызывает этот другой Git по URL.

Другой Git на данный момент перечисляет имена своих ветвей и идентификаторы ha sh, которые выбирают эти имена ветвей. , Например, если у них есть master и develop, ваш Git увидит это. Независимо от того, какие sh идентификаторы выбраны этими именами, ваш Git тоже увидит их.

Ваш Git теперь проверяет: есть ли у меня этот га sh идентификатор? Конечно, у вашего Git есть ничего , поэтому на этот раз ответ будет нет . (В будущем, возможно, ответ будет да . ) Ваш Git теперь просит их Git отправить этот коммит. Их Git должен предложить вашему Git родителю этого коммита его идентификатор ha sh. Ваши Git проверки: У меня есть этот коммит? Это продолжается до тех пор, пока они не отправят root коммит - коммит A, у которого нет родителя, или пока они не получат коммит, который вы уже иметь.

Таким образом, ваши Git и их Git договариваются о том, что у вас уже есть и что вам нужно от них. Затем они упаковывают все, что вам нужно - коммиты и все их файлы моментальных снимков, за исключением любых файлов, которые у вас уже могут быть в коммитах, которые у вас уже есть, - и отправляют все это снова. Поскольку это ваша самая первая операция извлечения, у вас вообще ничего нет, и они отправляют все заново, но в следующий раз, когда вы получите от них материал, они будут отправлять только что-нибудь новое .

Предположим, что они имеют:

A--B--C--D--E   <-- master (HEAD)
             \
              F   <-- feature

(ваш Git действительно видит их HEAD). Ваш Git будет запрашивать и получать каждый коммит и знать их расположение. Но ваши Git переименовывают их ветви: их master становится вашим origin/master, а их feature становится вашим origin/feature. Это ваши имена для удаленного слежения . Мы поймем, почему ваш Git делает это в данный момент.

Ваш Git завершил разговор со своими Git, поэтому ваш Git отключается от них и создает или обновляет ваш удаленный отслеживание имен. В вашем хранилище вы получите:

A--B--C--D--E   <-- origin/master
             \
              F   <-- origin/feature

Обратите внимание, что у вас еще нет master!

В качестве последнего шага git clone, ваш Git создает название ветви для вас. Вы можете выбрать, какую ветвь он должен создать - например, git clone -b feature означает create feature - но по умолчанию он смотрит на HEAD и использует это имя. Поскольку их HEAD выбрали их master, ваш Git создает вашу master ветвь:

A--B--C--D--E   <-- master (HEAD), origin/master
             \
              F   <-- origin/feature

Обратите внимание, что единственный простой способ найти коммит F по имени вашего удаленного трекинга origin/feature. У вас есть два простых способа найти коммит E: master и origin/master.

Теперь давайте сделаем новый коммит на master обычным способом. Этот новый коммит будет иметь в качестве своего родителя коммит E, и мы назовем новый коммит G:

              G   <-- master (HEAD)
             /
A--B--C--D--E   <-- origin/master
             \
              F   <-- origin/feature

Обратите внимание, что ваш master в вашем ноутбуке Git, теперь отклонено от их master на GitHub, которое ваш ноутбук Git помнит как origin/master.

Ваш master является вашей веткой , Вы можете делать с ней все, что хотите! Их master находится в их хранилище, которое они контролируют.

fetch vs pu sh

Давайте сделаем еще пару коммитов в наши master сейчас:

              G--H--I   <-- master (HEAD)
             /
A--B--C--D--E   <-- origin/master
             \
              F   <-- origin/feature

Обратите внимание, что до сих пор мы не отправляли никаких этих коммитов в другие Git. У нас есть коммиты G-H-I на нашем master, на ноутбуке или там, где есть наш Git, но у них их вообще нет.

Теперь мы можем запустить git push origin master. Наши Git вызовут их Git, как мы это делали раньше, но на этот раз вместо получения от них информации мы дадим им , начиная с commit I - обязательство, на которое указывают наши master. Мы спрашиваем их: У вас есть коммит I? Они этого не делают, поэтому мы спрашиваем их, есть ли у них H и G, а затем E. Они имеют имеют E, поэтому мы дадим им цепочку коммитов G-H-I.

Как только мы дадим им эти три коммита (и файлы, которые им нужны и у них нет - очевидно, у них есть E, поэтому у них есть свой снимок, поэтому наш Git может определить минимальный набор файлов для отправки), наш Git спрашивает их: Пожалуйста, если все в порядке, задайте ваш master указывает на I.

То есть мы просим их переместить их master ветвь с master имени, которое мы использовали в нашем git push origin master. У них нет mat1/master для отслеживания вашего хранилища. У них просто есть ветви .

Когда мы попросим их переместить свои master так, если они это сделают, они получат:

A--B--C--D--E--G--H--I   <-- master (HEAD)
             \
              F   <-- feature

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

              G--H--I   <-- master (HEAD), origin/master
             /
A--B--C--D--E
             \
              F   <-- origin/feature

(Упражнение: почему наш G находится на линии выше линии с E? Почему мы не нарисовали это так для их хранилища?)

git reset делает удаление коммитов локально легким

Теперь давайте предположим, что коммиты H и I плохие и вы хотели бы избавиться от них, Что мы можем сделать:

git checkout master             # if needed
git reset --hard <hash-of-G>

Что это будет делать, в нашем хранилище мы можем нарисовать так:

                 H--I   <-- origin/master
                /
A--B--C--D--E--G   <-- master (HEAD)
             \
              F   <-- origin/feature

Обратите внимание, что коммиты H и I не ушел . Они все еще там, в нашем репозитории, их можно найти, начиная с name origin/master, которое указывает на фиксацию I. Оттуда коммит I указывает на коммит H, который указывает на коммит G.

Наши master, тем не менее, указывают на коммит G. Если мы начнем отсюда, то увидим коммит G, затем коммит E, затем D и так далее. Мы не будем видеть совершать H-I вообще.

Что делает git reset, это позволяет нам указать имя текущей ветви , на которое HEAD прикреплен к любому коммиту в любом месте нашего полного репозитория . Мы можем просто выбрать этот коммит по его sh ID, который мы можем увидеть, запустив git log. Вырежьте и вставьте идентификатор ha sh из вывода git log в команду git reset --hard, и вы можете переместить имя текущей ветви в любой коммит.

Если мы сделаем это, и затем решите, gosh, что мы хотим коммитов H-I в конце концов, мы можем просто git reset --hard снова, с ha sh ID коммита I на этот раз, и заставить master указать на I снова. Таким образом, кроме разрушения любой незафиксированной работы, git reset --hard является своего рода безопасным. (Конечно, откладывать незавершенную работу - довольно много!)

Нам все еще нужно заставить их Git измениться: нам нужно больше силы

Перемещение наше master назад к G обновляется только нас . Другие Git на GitHub ... у них все еще есть их master, указывающие на коммит I.

Если мы запустим git push origin master сейчас, мы предложим затем совершите G. У них уже есть это, так они скажут так. Затем мы вежливо спросим их: Пожалуйста, если все в порядке, укажите master пункт для совершения G.

Они ответят нет ! Причина, по которой они скажут «нет», проста: если бы они это сделали, у них было бы:

                 H--I   [abandoned]
                /
A--B--C--D--E--G   <-- master (HEAD)
             \
              F   <-- feature

Их master больше не могут найти коммит I. На самом деле, у них есть нет способа найти коммит I. Коммит I становится потерянным, как и коммит H, потому что коммит I был таким, каким они нашли коммит H.

Чтобы убедить них сделать это в любом случае, нам нужно более сильная команда, а не просто вежливый запрос. Чтобы получить эту принудительную команду, мы можем использовать git push --force или git push --force-with-lease. Это меняет последний шаг нашего git push. Вместо того, чтобы спрашивать , пожалуйста, если все в порядке , мы отправляем команду: Установите ваш master! Или, с помощью --force-with-lease, мы отправляем самый сложный: I думаю, что ваши master очки для фиксации I. Если так, укажите вместо этого G! Тогда скажите мне, был ли я прав.

--force-with-lease действует как своего рода проверка безопасности. Если Git в списках GitHub не только для вас, но и для других Gits , возможно, некоторые другие Git заставили их master установить коммит J это основано на I. Если вы единственный, кто отправляет коммиты в ваш репозиторий GitHub, этого явно не произошло, и вы можете просто использовать обычный --force тип pu sh.

Опять: --force позволяет сказать другому Git: переместить имя каким-либо произвольным образом, что не обязательно просто add commits . Обычный git push добавляет коммитов в конец ветви, заставляя имя ветки указывать на коммит "дальше вправо", если вы рисуете свой график коммитов, как я был выше. git push, который удаляет коммитов, заставляет имя ветви указывать на более ранний коммит, теряя некоторую более позднюю фиксацию или, в наиболее сложных случаях, удаляет некоторые и добавляет другие коммиты. (Мы не рисовали этот случай, и мы оставим его для других ответов StackOverflow ... которые уже существуют, фактически, относительно перебазирования.)

Резюме

  • Поиск названий ветвей последний коммит в ветви (по определению).
  • Создание новых коммитов расширяет ветку, возвращая новую точку коммита к предыдущему совету и записывая большой уродливый случайный случай нового коммита -посмотреть ha sh ID в названии ветви.
  • git reset позволяет перемещать имена филиалов по своему усмотрению. Это может «удалить» коммиты из ветви (они все еще существуют в репозитории, и, возможно, есть другой способ их найти).
  • git push просит какой-то другой Git переместить его имена ветвей. По умолчанию разрешено только перемещение веток-имен, которое * add8 фиксирует.

Следовательно, поскольку вы выдвинули коммиты, которые хотите «удалить», вам нужно будет git reset свой собственный Git, но затем git push --force или git push --force-with-lease, чтобы заставить GitHub Git сделать то же самое.

Что HEAD^ et c о

Когда мы искали способы «удалить» коммиты с помощью git reset, я предложил:

  • Выполнить git log (и здесь я предлагаю использовать git log --decorate --oneline --graph, что так полезно, что вам, вероятно, следует создать псевдоним git dog, чтобы сделать это: DOG обозначает Decorate Oneline Graph).
  • коммит с вырезанным и вставленным га sh идентификаторами.

Это работает, и это прекрасный способ справиться с вещами. Хотя иногда это немного неуклюже. Что если бы можно было сказать: начиная с текущего коммита, отсчитать назад два коммита (или любое другое число)?

То есть у нас есть цепочка коммитов:

...--G--H--I--...

Мы можем выбрать один коммит в цепочке, например H, по его идентификатору ha sh или любым другим способом. Возможно, у него есть имя, указывающее на него:

...--G--H   <-- branchname
         \
          I--J   <-- master (HEAD)

Используя имя branchname, в общем, выберите commit H. Если для Git требуется фиксация ha sh ID, как в git reset, имя branchname будет получать га sh ID фиксации H.

Использование имени HEAD говорит Git: посмотрите на ветку, к которой присоединен HEAD, и используйте это имя . Поскольку HEAD присоединен к master, а master указывает на J, имя HEAD означает"commit J*, в местах, где Git требуется фиксация ha sh ID.

Это означает, что мы можем запустить:

git reset --hard branchname

прямо сейчас и получить:

...--G--H   <-- branchname, master (HEAD)
         \
          I--J   [abandoned]

имя branchname выбирает коммит H, а git reset перемещает имя ветви, к которой прикреплено HEAD - master - к коммиту, который мы выбираем.

Обратите внимание, кстати , что:

git reset --hard HEAD

означает: посмотрите на имя HEAD, чтобы выяснить, какой коммит мы сейчас используем, а затем выполните полный сброс, который перемещает текущее имя ветки в этот коммит. Но так как это коммит, который мы используем сейчас, это «перемещает» имя, чтобы указать на тот же коммит, на который он уже указывает. Другими словами, переместитесь туда, где вы уже стоите ... хорошо , не двигайтесь в конце концов . Так что этот git reset --hard позволяет нам выбрасывать незафиксированную работу, если это то, что нам нужно сделать.

В любом случае добавление одного карета ^ чара cter после любого имени ветви или ha sh ID означает выбрать родителя этого коммита . Так как branchname означает commit H, branchname^ означает commit G.

Если мы имеем:

...--F--G--H--I   <-- master (HEAD)

затем master^ и HEAD^ оба означают коммит H. Добавление еще ^ говорит Git, что нужно сделать это снова: master^^ или HEAD^^ означает commit G. Добавление третьего ^ выбирает коммит F и т. Д.

Если вы хотите сосчитать пять коммитов назад, вы можете написать master^^^^^ или HEAD^^^^^, но проще в использовании master~5 или HEAD~5. Как только вы пройдете «два шага назад», обозначение тильды ~ будет короче. Обратите внимание, что вы всегда можете использовать нотацию тильды: master~1 и master^ оба считают один коммит обратно. Здесь вы также можете опустить 1 и написать master~ или HEAD~.

(Там равно разница между ^2 и ~2. Подробнее об этом см. документация по gitrevision .)

1 голос
/ 07 марта 2020

git revert создает новый коммит, отменяя изменения из данного коммита. Кажется, что описанная вами операция дает желаемый результат.

Читайте также: Как отменить (почти) что-нибудь с помощью Git .

...