Кто такие «они» и «мы» в «git revert»?

Я не могу понять, кто us и them участвует в этих конфликтах во время git revert, поэтому я действительно не знаю, что здесь происходит:

git revert some_commit_hash

Затем git status показывает следующие конфликты:

deleted by them: path/to/file1.h
both modified:   path/to/file2.h
deleted by them: path/to/file1.cpp
deleted by them: path/to/test_file1.cpp
added by us:     path/to/file3.h
deleted by them: path/to/file4.h
added by us:     path/to/file5.h

Кто мы? Кто они?

Обновление: обратите внимание, что коммит, который я возвращаю, является очень большим коммитом слияния.


НЕ дублирует:

  1. потому что это не уточняет, кто такие us и them: GIT: насколько опасно удаляется нами конфликт?
  2. потому что он охватывает merge и rebase, но НЕ revert, и git часто использует одни и те же термины для обозначения противоположных вещей в зависимости от операции: Кто такие мы и кто они согласно Git?
  3. потому что он не упоминает нас и их - Git - откат возврата, конфликты

person Gabriel Staples    schedule 15.09.2020    source источник
comment
Я не знаю этого сразу, но это должно быть достаточно просто, чтобы понять: 1) git show some_commit_hash и искать добавленные/удаленные файлы. 2) Сопоставьте их с текущим git status (с обратным добавлением/удалением, потому что вы возвращаетесь). 3) Прибыль.   -  person 0x5453    schedule 15.09.2020
comment
Я все еще думаю, что это дубликат Who are us and кто они согласно Git? - вы должны отредактировать этот вопрос, чтобы включить раздел о revert, если он отличается от других.   -  person pkamb    schedule 15.09.2020
comment
@pkamb, нет, см. № 2 в разделе НЕ дубликаты в моем вопросе. Я предвидел, что это произойдет.   -  person Gabriel Staples    schedule 15.09.2020
comment
@GabrielStaples Я думаю, что pkamb предлагает обновить вопрос / ответ, чтобы сделать его дубликатом, а не добавлять здесь новый ответ.   -  person chepner    schedule 15.09.2020
comment
@chepner, понятно. Одна проблема с этим, однако, заключается в том, что я регулярно добавляю новые ответы на вопросы от 6 месяцев до 13 лет (Я получил значок некроманта 26 раз), у большинства людей его нет, поэтому редактирование этого вопроса вряд ли когда-либо привлечет к нему внимание, необходимое для получения ответа, который я ищу.   -  person Gabriel Staples    schedule 15.09.2020
comment
Если вы получите здесь хороший ответ, вы можете рассмотреть возможность редактирования канонического вопроса/ответа и закрытия его как дубликата. [Некромант х 52 :)]   -  person pkamb    schedule 15.09.2020
comment
Кроме того, я не уверен, что формирование чужого вопроса, чтобы расширить его, чтобы также охватить мой вопрос, - это лучшее, что можно сделать. Я полагаю, что есть аргумент в обе стороны, но я обычно против того, чтобы чей-то вопрос расширялся за пределы того, что изначально спрашивал спрашивающий. Он или она пометил ответ как правильный, который не распространяется на мой вопрос, поэтому очевидно, что у спрашивающего был другой вопрос, чем у меня сейчас.   -  person Gabriel Staples    schedule 15.09.2020
comment
Другой вопрос задается после перебазирования Git, и в других обстоятельствах...   -  person pkamb    schedule 15.09.2020


Ответы (4)


Когда возникает конфликт, во всех ситуациях применяется правило:

  • ours/us — это состояние текущего HEAD (активный коммит)
  • theirs/them — это состояние другой стороны (слияние фиксации, выборка/перебазирование фиксации или, в вашем случае, обратная фиксация, которую вы хотите отменить)

Некоторые дополнительные разъяснения в случае rebase (отвечая на комментарий @GabrielStaples):

если вы находитесь на my/branch и запускаете git rebase other/branch, git проверит главный коммит other/branch и начнет воспроизводить некоторые коммиты сверху.

Если возникает конфликт, поскольку проверенный коммит исходит от other/branch, ours будет приблизительно представлять other/branch, а theirs будет my/branch.

Эта часть противоречит интуиции ours должно быть моими изменениями, но она соответствует приведенному выше описанию: на момент конфликта проверенный коммит — ours, другая сторона (воспроизводимый коммит) — theirs.

person LeGEC    schedule 15.09.2020
comment
Я не думаю, что это правильно: When you rebase, us refers the upstream branch, and them is the branch you're moving about. It's a bit counter-intuitive in case of a rebase. stackoverflow.com/a/21025695/4561887. Следовательно, git rebase HEAD~3 означает, что us равно HEAD~3, а them равно HEAD, что прямо противоположно тому, что говорится в вашем ответе. Это связано с тем, что с точки зрения git (я так думаю?) это на самом деле просто отбор нисходящих коммитов, поэтому он проверил HEAD~3, так что теперь он является его активным HEAD, а затем он продолжает выбирать нисходящие коммиты по одному в -время. - person Gabriel Staples; 15.09.2020
comment
Вы дали объяснение во второй части своего комментария, я отредактировал свой ответ, чтобы подчеркнуть, как применяется правило ours/theirs в случае перебазирования. - person LeGEC; 15.09.2020
comment
Насчет rebase, это правильно. При перемещении base branch это то, что находится на HEAD (следовательно, мы)... а то, что вы перемещаете, будет the other branch. ezconflict.com/en/conflictsse12.html#x53-890001.7 (отказ от ответственности: мой материал, без файлов cookie, без отслеживания, без монетизации) - person eftshift0; 15.09.2020
comment
Я проголосовал за ваш ответ, сослался на него и изложил его в своем ответе, охватывающем все 4 случая здесь: stackoverflow.com/questions/21025314/ - person Gabriel Staples; 16.09.2020
comment
@eftshift0, ваша ссылка, похоже, имеет тег (#x53-890001.7), но она неправильно переходит в любое место на странице. На какой части этой страницы вы бы хотели, чтобы я сосредоточился? (Рекомендация: я настоятельно рекомендую вам взглянуть на использование страниц Github для веб-сайтов с личным кодом — вы можете написать в уценке - я тоже настраиваю свой). - person Gabriel Staples; 16.09.2020

Хотя на это уже довольно хорошо ответили, есть еще один способ взглянуть на все это. Так на это смотрит сам Git. Все четыре операции — выбор вишни, слияние, перебазирование и возврат — используют один и тот же механизм, а флаги --ours и --theirs для git checkout, а также расширенные параметры -X ours и -X theirs в конечном итоге ссылаются на одни и те же вещи, используя одни и те же внутренние код. Мне нравится называть этот механизм слиянием как глаголом, потому что сначала мы знакомимся с ним через git merge, когда слияние должно выполнять настоящее слияние.

Случай слияния

При выполнении реального слияния термины имеют смысл. Начнем с того, что можно проиллюстрировать следующим образом:

          I--J   <-- ourbranch (HEAD)
         /
...--G--H
         \
          K--L   <-- theirbranch

Здесь имя ourbranch выбирает фиксацию J, которая является нашей фиксацией в нашей ветке (в данном случае это одна из двух таких фиксаций, хотя количество фиксаций исключительно в нашей собственной ветке должно быть не менее 1, чтобы вызвать настоящее слияние). Имя theirbranch выбирает фиксацию L, которая является их фиксацией в их ветке (опять же, одна из двух, причем здесь необходима по крайней мере одна фиксация).

Что Git делает, чтобы выполнить это слияние — объединить как глагол некоторый набор файлов — для каждого файла во всех трех коммитах H, J и L сравнивает файл в H с этим в J, чтобы увидеть, что мы изменили, и сравнить файл в H с файлом в L, чтобы узнать, что они изменили. Затем Git объединяет эти два набора изменений, применяя объединенные изменения ко всему, что находится в H.

Коммит H — это база слияния коммит, коммит J — наш коммит, а коммит L — их коммит. Любое различие, будь то новый файл, добавленный нами, или файл, удаленный ими, или что-то еще, относится к коммиту H.

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

  1. Настраивать:

    • read merge base commit (H) into index at slot 1
    • прочитать ours коммит (HEAD = J) в индекс в слоте 2
    • прочитать theirs commit (L) в индекс в слоте 3
  2. Определите одинаковые файлы. Обратите внимание, что шаги 2 и 3 повторяются для каждого файла.

    • if there's a file named F in all three slots, it's the same file
    • в противном случае, если в слоте 1 есть что-то, попробуйте угадать переименования, которые свяжут базовый файл слияния в слоте 1 с нашим или их файлом с другим именем, который находится в слоте 2 и/или слоте 3; если не удается найти файл для переименования, наша и/или их сторона удалила этот файл; эти случаи также могут привести к конфликту высокого уровня, такому как переименование/изменение или переименование/удаление, когда мы объявляем конфликт и продолжаем, не выполняя шаг 3.
    • в противном случае (ничего в слоте 1, но что-то в слотах 2 и 3) мы имеем конфликт добавления/добавления: объявить этот конкретный файл конфликтующим и двигаться дальше, не выполняя шаг 3
  3. Замкните легкие случаи и сделайте сложные случаи с помощью низкоуровневого слияния:

    • if the blob hash IDs in slots 1, 2, and 3 all match, all three copies are the same; use any of them
    • если хеш-идентификатор большого двоичного объекта в слоте 1 совпадает с идентификатором в слоте 2 или 3, значит, кто-то не изменил файл, а кто-то изменил; использовать измененный файл, т. е. взять файл, который отличается
    • otherwise, all three slots differ: do a changed-block-of-lines by changed-block, low-level merge
      • if there's a merge conflict during the low level merge, -X ours or -X theirs means "resolve the conflict using ours/theirs" where ours is whatever is in slot 2 and theirs is whatever is in slot 3
      • обратите внимание, что это означает, что везде, где нет конфликта, например, только одна сторона изменила строку 42, расширенный вариант -X вообще не применяется, и мы принимаем изменение, независимо от того, является оно нашим или их

В конце этого процесса любой полностью разрешенный файл перемещается обратно в его обычное положение нулевого слота, при этом записи слотов 1, 2 и 3 удаляются. Любой неразрешенный файл остается со всеми тремя занятыми индексными слотами (в конфликтах удаления и добавления/добавления некоторые слоты пусты, но используется какой-то слот с ненулевым номером стадии, что помечает файл как конфликтующий ).

Следовательно, объединить или объединить как глагол работает в индексе Git.

Все описанные выше действия происходят в индексе Git с побочным эффектом, когда обновленные файлы остаются в вашем рабочем дереве. Если есть конфликты низкого уровня, ваши файлы рабочего дерева помечаются маркерами конфликта и различными разделами из строк, соответствующих копиям файлов, которые находятся в слотах индекса 1 (база слияния), 2 (наша) или 3 (их).

В конечном итоге это всегда сводится к одному и тому же уравнению: 1 = база слияния, 2 = наша, 3 = их. Это верно, даже если команда, которая загружает индекс, не git merge.

Вишневый выбор и возврат используют механизм слияния

Когда мы запускаем git cherry-pick, у нас есть график коммитов, который выглядит так:

...--P--C--...
   \
    ...--H   <-- somebranch (HEAD)

Буквы P и C здесь обозначают любую пару коммитов родитель-потомок. C может даже быть фиксацией слияния, если мы используем параметр -m для указания какого родителя использовать. (Нет никаких реальных ограничений на то, где три коммита живут на графике: я нарисовал его с H дочерним элементом некоторого коммита, который идет до P, но он может быть после пары P-C, как в ...-E-P-C-F-G-H, например, или может быть не должно быть никакой связи между коммитами P-C и H, если у вас есть несколько непересекающихся подграфов.)

Когда мы запускаем:

git cherry-pick <hash-of-C>

Git сам найдет коммит P, используя родительскую ссылку от C до P. P теперь действует как база слияния и считывается в индексный слот 1. C действует как --theirs коммит и считывается в индексный слот 3. Наш текущий коммит H является --ours коммитом и считывается во второй слот индекса. механизм работает сейчас, поэтому наша фиксация — HEAD, а их фиксация — C, с базой слияния, которая появляется, если мы устанавливаем merge.conflictStyle на diff3 или если мы используем git mergetool для запуска инструмента слияния, — это фиксация P.

Когда мы запускаем:

git revert <hash-of-C>

происходит то же самое, за исключением того, что на этот раз коммит C — это база слияния в слоте 1, а коммит P — это коммит --theirs в слоте 3. Коммит --ours в слоте 2, как обычно, из HEAD.

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

git cherry-pick stop..start

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

...--C--D--E--...
 \
  H   <-- HEAD

git cherry-pick C..E сначала копирует D, затем E, но git revert C..E сначала возвращает E, а затем D. (Коммит C не играет роли, поскольку синтаксис с двумя точками исключает фиксации, достижимые с левой стороны выражения с двумя точками. См. документацию gitrevisions для получения дополнительной информации.)

Rebase - это повторный выбор вишни

Команда rebase работает путем повторного запуска git cherry-pick, после использования git checkout --detach или git switch --detach для перехода в режим отсоединенного HEAD. (Технически теперь это делается только внутри; в старые времена некоторые версии git rebase, основанные на сценариях оболочки, действительно использовали git checkout, хотя и с хэш-идентификатором, который все равно всегда переходил в отсоединенный режим.)

Когда мы запускаем git rebase, мы начинаем примерно так:

       C--D--E   <-- ourbranch (HEAD)
      /
...--B--F--G--H   <-- theirbranch

Мы бежим:

git checkout ourbranch   # if needed - the above says we already did that
git rebase theirbranch   # or, git rebase --onto <target> <upstream>

Первое — ну, второе — что это делает, это входит в режим отсоединения HEAD, при этом фиксация HEAD является фиксацией, которую мы выбрали с нашим аргументом --onto. Если бы мы не использовали отдельный флаг и аргумент --onto, --onto брался бы из одного аргумента, который мы указали, в данном случае theirbranch. Если мы не использовали отдельный аргумент upstream, указанный нами аргумент — в данном случае theirbranch — используется для обеих целей.

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

git rev-list --topo-order --reverse <hash-of-upstream>..HEAD

в данном случае это хэш-идентификаторы коммитов C, D и E: три коммита, которые доступны из ourbranch, но недоступны из theirbranch.

После того, как git rebase сгенерировал этот список и перешел в режим detached-HEAD, то, что мы имеем сейчас, выглядит так:

       C--D--E   <-- ourbranch
      /
...--B--F--G--H   <-- theirbranch, HEAD

Теперь Git запускает один файл git cherry-pick. Его аргументом является хэш-идентификатор коммита C, первого коммита, который нужно скопировать. Если мы посмотрим выше на то, как работает выбор вишни, мы увидим, что это операция слияния как глагола, при этом база слияния является родителем C, то есть фиксация B, текущая фиксация или --ours фиксация H, и фиксация, подлежащая копированию или --theirs, является фиксацией C. Вот почему наши и их кажутся противоположными.

Однако, как только эта операция выбора вишни завершена, мы теперь имеем:

       C--D--E   <-- ourbranch
      /
...--B--F--G--H   <-- theirbranch
               \
                C'  <-- HEAD

Теперь Git копирует коммит D с git cherry-pick. Базой слияния теперь является фиксация C, фиксация --ours — фиксация C', а фиксация --theirsD. Это означает, что и наши, и их коммиты являются нашими, но на этот раз наш коммит — это тот, который мы только что создали несколько секунд (или миллисекунд) назад!

Он основан на существующей фиксации H, которая принадлежит им, но это фиксация C', которая принадлежит нам. Если мы получаем какие-либо конфликты слияния, они, несомненно, являются результатом того, что они основаны на H, возможно, включая какое-то разрешение конфликтов, которое мы выполнили вручную, чтобы создать C'. Но в буквальном смысле все три входных коммита наши. Слот индекса № 1 — из фиксации C, слот индекса № 2 — из фиксации C', а слот индекса № 3 — из фиксации D.

Как только мы все это сделали, наша картина теперь:

       C--D--E   <-- ourbranch
      /
...--B--F--G--H   <-- theirbranch
               \
                C'-D'  <-- HEAD

Теперь Git запускает git cherry-pick на хеше коммита E. База слияния — это коммит D, а наши и их коммиты — D' и E соответственно. Итак, еще раз, во время перебазирования все три коммита наши, хотя конфликты слияния, вероятно, являются результатом построения на H.

Когда последний выбор сделан, Git завершает перебазирование, беря имя ourbranch из старого коммита E и вставляя его в новый коммит E':

       C--D--E   [abandoned]
      /
...--B--F--G--H   <-- theirbranch
               \
                C'-D'-E'  <-- ourbranch (HEAD)

Теперь мы вернулись к обычному режиму работы с прикрепленной головкой, и поскольку git log начинается с того места, где мы находимся сейчас — с момента фиксации E' — и работает в обратном направлении, что никогда не посещает исходную фиксацию C, кажется, что мы каким-то образом изменили исходные три совершает. У нас нет: они до сих пор там, в нашем репозитории, доступны через специальную псевдо-рефу ORIG_HEAD и доступны через наши рефлоги. Мы можем вернуть их по крайней мере на 30 дней по умолчанию, после чего git gc сможет их пожинать, а затем они действительно исчезнут. (Ну, пока мы не git push переместили их в какой-нибудь другой репозиторий Git, где они все еще хранятся.)

person torek    schedule 16.09.2020
comment
Это похоже на действительно отличный ответ. Я с нетерпением жду возможности изучить его, когда у меня будет шанс. Спасибо за добавление. - person Gabriel Staples; 20.09.2020
comment
Я весь красный. Спасибо. Вы, должно быть, внесли свой вклад в исходный код git, чтобы получить эти глубокие знания? Это очень полезная информация. для меня тоже: мы можем вернуть их как минимум на 30 дней по умолчанию, после чего git gc будет чувствовать себя свободно, чтобы пожинать их, и тогда они действительно исчезнут. Я никогда не знал, как долго git оставит заброшенные HEADS/коммиты. - person Gabriel Staples; 08.01.2021
comment
Ключевым выводом для меня здесь относительно git revert является, я думаю, что если у вас есть линейное дерево ...A--B--C--D(HEAD), где D является вашим текущим HEAD, и вы делаете git revert B, то B, тот самый коммит, который вы пытаетесь вернуть, становится текущим merge-base, или Слот 1 в этом слиянии, Слот 2 или наш становится D/HEAD, а Слот 3 или их становится A, или родительский коммит возвращается, правильно? Затем выполняется низкоуровневое слияние, в результате которого применяются все изменения из B..D, а также все изменения из B..A, тем самым возвращая B, верно? Это трудно. - person Gabriel Staples; 08.01.2021
comment
Пожалуйста, ознакомьтесь с моим обновленным разделом «Результаты и выводы» моего ответа здесь и скажите, все ли у меня правильно. - person Gabriel Staples; 08.01.2021
comment
Я действительно внес свой вклад (в последнее время, правда, немного). У вас, кажется, это прямо сейчас. Что касается того, как долго остаются заброшенные коммиты: это действительно находится под контролем reflogs Git. Записи reflog делают коммиты доступными. Запуск git gc запускает, среди прочего, git reflog expire со временем истечения срока действия, установленным вашими настройками времени GC. Отсюда берутся значения по умолчанию 30 и 90 дней (см. также документацию git config). После того, как срок действия записей в журнале ссылок истечет, а коммиты станут действительно недоступны, [продолжение] - person torek; 08.01.2021
comment
По истечении срока действия упакованный объект можно извлечь из пакета с помощью git repack, а незакрепленный объект можно удалить с помощью git prune. Поскольку git gc запускает оба эти (с различными интервалами), теперь незащищенные коммиты становятся небезопасными в этот момент, но когда они действительно исчезают, это гораздо более случайно, поскольку процесс gc зависит от накопления в подкаталогах .git . Это может сработать быстро, а может занять месяцы. - person torek; 08.01.2021
comment
И да, мелкие детали, находящиеся на самых нижних уровнях Git, сложны. :-) Общая теория и идеи вполне разумны, но реализация полна хаков, компромиссов и прагматичных, но спорных решений. - person torek; 08.01.2021

TLDR;

Перейти к самому низу для результатов и выводов.

Подробности:

Касательно:

Затем git status показывает следующие конфликты:

deleted by them: path/to/file1.h
both modified:   path/to/file2.h
deleted by them: path/to/file1.cpp
deleted by them: path/to/test_file1.cpp
added by us:     path/to/file3.h
deleted by them: path/to/file4.h
added by us:     path/to/file5.h

Я провел несколько экспериментов и заметил следующее.

Во-первых, я вручную разрешил только конфликты в оба измененных файлах, path/to/file2.h, как обычно для любого конфликта перебазирования или слияния. Затем я добавил все файлы и завершил восстановление:

git add -A
git revert --continue

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

git log --diff-filter=A -- path/to/file

Это показывает git log commit_hash только для одного единственного commit_hash, который создал этот файл. Я сделал это по одному для каждого файла, который был удален ими или добавлен нами:

git log --diff-filter=A -- path/to/file1.h        # added by the commit I reverted
git log --diff-filter=A -- path/to/file1.cpp      # added by the commit I reverted
git log --diff-filter=A -- path/to/test_file1.cpp # added by the commit I reverted
git log --diff-filter=A -- path/to/file3.h        # added by a later commit
git log --diff-filter=A -- path/to/file4.h        # added by the commit I reverted
git log --diff-filter=A -- path/to/file5.h        # added by a later commit

Я обнаружил, что 4 файла, как указано выше, были добавлены фиксацией, которую я отменил. Обратите внимание, это означает, что они были добавлены самим коммитом some_commit_hash, а НЕ коммитом возврата, который был создан при запуске git revert some_commit_hash. Итак, почему они все еще существовали, если я отменил этот коммит? Что ж, оказывается, более поздняя фиксация, которую мы назовем later_commit_hash, которая произошла ПОСЛЕ some_commit_hash, затронула все 6 этих файлов, изменив 4 из них и создав 2 из них.

Давайте сгруппируем вышеуказанные файлы по группам удаленные ими и добавленные нами:

# deleted by them:
path/to/file1.h
path/to/file1.cpp
path/to/test_file1.cpp
path/to/file4.h

# added by us:
path/to/file3.h
path/to/file5.h

Теперь укажите, какой файл каким коммитом был добавлен:

# deleted by them / added by the commit I reverted (`some_commit_hash`)
path/to/file1.h
path/to/file1.cpp
path/to/test_file1.cpp
path/to/file4.h

# added by us / added by a later commit (`later_commit_hash`)
path/to/file3.h
path/to/file5.h

Итак, вы можете видеть, что удаленные ими файлы были добавлены фиксацией, которую я отменил, а это означает, что отмена этой фиксации удалит эти файлы! Таким образом, them относится к отменяемому коммиту, some_commit_hash, а us относится к оставшимся коммитам в HEAD.

Конфликт был в том, что later_commit_hash коснулся тех 4-х удаленных им файлов, поэтому git revert some_commit_hash не смог их удалить. И 2 добавленных нами файла НЕ существовали до some_commit_hash, поэтому конфликт был в том, что они не должны были существовать после реверта, но они существовали, потому что были созданы later_commit_hash.

Решение, которое я сделал, это вручную удалить все эти 6 файлов:

rm path/to/file1.h
rm path/to/file1.cpp
rm path/to/test_file1.cpp
rm path/to/file3.h
rm path/to/file4.h
rm path/to/file5.h

затем я зафиксировал это изменение как новый коммит:

git add -A
git commit

Однако вместо этого я мог бы вернуться к местоположению, предшествовавшему возврату коммита, и сначала отменить later_commit_hash, а затем отменить some_commit_hash секунду, эффективно откатив эти изменения по порядку, например так:

git reset --hard HEAD~  # WARNING! DESTRUCTIVE COMMAND! BE CAREFUL.
git revert later_commit_hash
git revert some_commit_hash
# should result in no conflicts during both of those reverts now

Результаты и выводы:

В любом случае, чтобы ответить на мой собственный вопрос:

Во время git revert some_commit_hash:

  1. нас = проверенный в данный момент коммит (то есть: HEAD) на момент ввода и запуска git revert some_commit_hash, и:
  2. them = (обратный или противоположный?) коммит, который вы отменяете; то есть: это некоторая эфемерная фиксация, противоположная some_commit_hash, чтобы отменить изменения some_commit_hash, предполагая, что вы запускаете команду git revert some_commit_hash.

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

Ключевым выводом для меня здесь относительно git revert является, я думаю, что если у вас есть линейное дерево ...A--B--C--D(HEAD), где D является вашим текущим HEAD, и вы делаете git revert B, то B, тот самый коммит, который вы пытаетесь вернуть, становится текущим merge-base, или Слот 1 в этом слиянии, Слот 2 или наш становится D/HEAD, а Слот 3 или их становится A, или родительский коммит возвращается, правильно? Затем выполняется низкоуровневое слияние, в результате которого применяются все изменения из B..D, а также все изменения из B..A, тем самым возвращая B, верно? Это трудно.

Итак, это означает, что эта эфемерная фиксация, противоположная some_commit_hash, на самом деле является просто обратным diff или diff в направлении from some_commit_hash, вы возвращаетесь к его родительскому коммиту. . Теперь у вас под капотом происходит низкоуровневое слияние git, где база слияния — some_commit_hash для возврата, наш/нас — HEAD, а их/их — родитель some_commit_hash, также известный как some_commit_hash~. Когда git выполняет это низкоуровневое слияние, разница между some_commit_hash и HEAD (то есть: эквивалент git diff some_commit_hash..HEAD) фиксирует весь ваш новый контент, а разница между some_commit_hash и его родителем (то есть: эквивалент git diff some_commit_hash..some_commit_hash~) захватывает отменить изменения, сделанные коммитом some_commit_hash, тем самым отменив этот коммит!

Если я все правильно понял, теперь все имеет смысл!


Я все еще немного борюсь с этой концепцией, но суть в этом. Я думаю, что точная механика того, как работает revert, действительно прояснит ситуацию. Этот ответ может дать больше информации, но я его не понимаю.

Я также только что добавил сюда ответ, чтобы разъяснить нам и им для всех 4 операций git, о которых я могу думать, где это может произойти: git merge, git cherry-pick, git rebase и git revert: Кто мы и кто они согласно Git?


(Примечания для себя):

Нужно взглянуть на: http://ezconflict.com/en/conflictsse12.html#x53-890001.7

person Gabriel Staples    schedule 16.09.2020
comment
Что git show --name-status some_commit_hash говорит о двух файлах, которые отображаются как added by us? - person eftshift0; 16.09.2020
comment
@eftshift0, их вообще нет, потому что они были добавлены later_commit_hash, который шел после some_commit_hash. - person Gabriel Staples; 16.09.2020
comment
Однако, когда я делаю git show --name-status later_commit_hash, я вижу: R100 path/to/file3_old_name.h path/to/file3.h и R100 path/to/file5_old_name.h path/to/file5.h, показывая, что они были переименованы (хотя я не знаю, что именно означает R100). - person Gabriel Staples; 16.09.2020
comment
r100 означает, что содержимое не изменилось. Ok. Позвольте мне переварить все это. Спасибо за информацию. - person eftshift0; 16.09.2020

Ну... revert очень особенная ситуация. Итак, подумайте об обычном слиянии, с общим предком и всем остальным, целым пакетом, верно? Теперь все работает так же, как слияние, за исключением (и это большое исключение), что механизм слияния принуждает the common ancestor быть ревизией, которую вы пытаетесь вернуть, и the other branch является родителем этой версии.

person eftshift0    schedule 15.09.2020
comment
Я пытаюсь понять, что вы говорите, так как я действительно думаю, что вы что-то здесь понимаете, но я не могу понять, что именно вы имеете в виду. Однако в этой ситуации с возвратом что-то отличается, и я думаю, что мой ответ близок, но еще не совсем подходит. - person Gabriel Staples; 16.09.2020
comment
Вы знакомы с тем, как происходит слияние? Например, the common ancestor и 2 подсказки? Если да, то это простое слияние, ваша ветвь - это ваша ветвь, а общий предок фиксирован для ревизии, которую вы возвращаете, другая ветвь является ее родителем. Попробуйте нарисовать это в своей голове. Если вы не знакомы с этими понятиями, перейдите на тот же сайт, на который я ссылался ранее, и проверьте раздел, посвященный 3-сторонней монете. - person eftshift0; 16.09.2020
comment
Да, я не знаком с тем, как слияние на самом деле работает под капотом. Я вижу вашу ссылку здесь (ezconflict.com/en/conflictsse12.html#x53-890001.7< /а>). Я проверю. - person Gabriel Staples; 16.09.2020
comment
Вы видите этот конфликт в открытом проекте? Мне действительно хотелось бы посмотреть, что происходит. - person eftshift0; 16.09.2020
comment
И он на самом деле не вдается в подробности о том, как работает слияние.... он, по крайней мере, говорит вам, каковы 3 вещи, которые рассматриваются для слияния.... и< /i> он умоляет вас использовать diff3 (что не будет иметь значения для вас.... вы имеете дело с конфликтами деревьев, и я еще не начал с этого раздела... позже ). - person eftshift0; 16.09.2020
comment
Are you seeing this conflict on an open project? I would really like to see what's going on. Нет, извините, это закрытый проект, которым я не могу поделиться. - person Gabriel Staples; 16.09.2020