Как мне перемещаться вперед и назад между коммитами в git?

107

Я делаю git bisectи после достижения проблемного коммита теперь пытаюсь сделать шаг вперед / назад, чтобы убедиться, что я нахожусь в правильном положении.

Я знаю, HEAD^как вернуться назад в истории, но есть ли еще один ярлык, который поможет мне двигаться вперед (к конкретному коммиту в будущем), например:

A - B - C(HEAD) - D - E - F

Я знаю , что моя цель F , и я хочу , чтобы перейти от C до D .


ПРИМЕЧАНИЕ: это не дубликат Git: как перемещаться между коммитами , мой вопрос немного отличается, и на него нет ответа.

Костас
источник
1
stackoverflow.com/questions/2263674/… тоже может помочь.
VonC
git checkout -b new_branch HEAD~4чтобы вернуться на 4 коммита из HEAD, как в stackoverflow.com/a/4940090/911945
Антон Тарасенко

Ответы:

59

Я немного поэкспериментировал, и это, похоже, помогает перемещаться вперед ( редактировать : он хорошо работает только тогда, когда у вас есть линейная история без коммитов слияния):

git checkout $(git rev-list --topo-order HEAD..towards | tail -1)

где towardsSHA1 фиксации или тега.

Пояснение:

  • команда внутри $()означает: получить все коммиты между текущим HEADи towardsфиксацией (исключая HEAD) и отсортировать их в порядке приоритета (как в git logпо умолчанию - вместо хронологического порядка, который, как ни странно, по умолчанию rev-list), а затем взять последний ( tail), то есть тот, к которому мы хотим перейти.
  • это оценивается в подоболочке и передается git checkoutдля выполнения проверки.

Вы можете определить функцию, доступную как псевдоним ожидания параметра в вашем .profileфайле, чтобы перейти к конкретной фиксации:

# Go forward in Git commit hierarchy, towards particular commit 
# Usage:
#  gofwd v1.2.7
# Does nothing when the parameter is not specified.
gofwd() {
  git checkout $(git rev-list --topo-order HEAD.."$*" | tail -1)
}

# Go back in Git commit hierarchy
# Usage: 
#  goback
alias goback='git checkout HEAD~'
jakub.g
источник
2
Движение вперед отлично работает с прямыми частями истории, но зацикливается при слиянии.
Костас
Да, на самом деле я не тестировал его на слияниях. Я постараюсь посмотреть в свободное время, но у меня временно мало стимула, так как мы договорились о строго линейной истории в нашем проекте;)
jakub.g
3
Отличный ответ! Изменено, чтобы автоматически указывать текущую ветку: stackoverflow.com/a/23172256/480608
Рейн Ревер
Это неверно. git logпоказывает коммиты в хронологическом порядке, по умолчанию так же, как rev-list, за исключением случая использования --graphфлага.
papiro
Довольно убедительное свидетельство того, что git слишком сложен. Для чего-то такого простого, как Undo или Redo, у нас есть сумасшедший список противоречивых ответов. И я даже не вдавался ни в один из связанных ответов. FWIW, я попробовал вариант этого с простым линейным набором коммитов и, наконец, просто сдался.
Snowcrash
49

Все, что вам нужно для ясного, а не отсоединенного состояния головки, - это сброс, а не проверка.

git reset HEAD@{1}
d3day
источник
5
или git reset "HEAD@{1}"в определенных оболочках, таких как fish и powershell ... git reflogтакже может быть полезно для поиска правильной фиксации.
Стив Кук
1
Всегда используйте одинарные кавычки в командах оболочки, если вы явно не хотите, чтобы оболочка пыталась интерпретировать / расширять содержимое. Это особенно важно в таких случаях, когда цель состоит в том, чтобы предотвратить интерпретацию командным интерпретатором специальных символов. Таким образом, вам не нужно знать, содержит ли строка что-нибудь проблемное.
Крис Пейдж
Я сделал это, чтобы оглянуться назад во времени, потом я сделал, git reset HEADчтобы вернуться туда, где я был ... теперь я понятия не имею, в каком состоянии находится мой репозиторий, и все страшно. Что мне теперь делать?
theonlygusti
48

Я верю, что вы можете:

git reset HEAD@{1}

Сделать один коммит вперед во времени. Чтобы перейти к нескольким коммитам, используйте HEAD @ {2}, HEAD @ {3} и т. Д.

w0utert
источник
20

Это то, что я использую для навигации взад и вперед.

переход к следующей фиксации

function n() {
    git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}

переход к предыдущей фиксации

function p() {
    git checkout HEAD^1
}
МК
источник
Спасибо! Я использую это сейчас! Примечания для других новичков, таких как я : чтобы повторно подключить HEAD, git checkout <current branch>прикрепляется к последней фиксации. git checkout -b <new-branch-name>из текущего коммита позволяет вносить изменения в новую ветку.git rebase -iтоже работает. Кроме того , я назвал свою n()функцию, nx()чтобы избежать конфликта с диспетчером версий узлов "n". Обязательно проверьте псевдонимы!
Стивен Чой
function () {...} предназначена для создания файла сценария bash для Unix / Linux, я работаю в Windows, что сначала мне немного сложно понять
IcyBrk
9

Скажем, F - это последний коммит trunk(вставьте здесь свое собственное имя ветки) ... вы можете ссылаться на него как trunk~0(или просто trunk), E как trunk~1, D как trunk~2и т. Д.

Загляните в свой рефлог, чтобы узнать о других способах называть коммиты.

Бесполезный
источник
1
~ идет назад, а не вперед, ствол ~ 2 - A
EmmanuelMess
Да. В этом ответе предполагается, что у вас есть ветка с именем trunk, на которую указывает F, и что вы знаете, куда в истории этой ветки вы хотите перейти. Он не пытается двигаться вперед относительно HEAD, но менее далеко назад относительно туловища.
Бесполезный
@theonlygusti Вы дважды возвращаетесь из HEAD.
EmmanuelMess
Вы все еще предполагаете, что ветка trunkи ваш ток HEADидентичны, что не показано в вопросе, что, как я сказал, не является тем, что я предполагаю, и что очень маловероятно на полпути черезbisect
Бесполезно
3

Обратный ход тривиален, так как вы движетесь вниз по дереву, и всегда есть один путь.

  function git_down
        git checkout HEAD^
  end

При перемещении вперед вы движетесь вверх по дереву, поэтому вам нужно четко указать, на какую ветку вы нацеливаетесь:

  function git_up 
        git log --reverse --pretty=%H $argv | grep -A 1 (git rev-parse HEAD) | tail -n1 | xargs git checkout
  end

Использование: git down,git up <branch-name>

Дзиамид
источник
Обратное движение тоже не совсем уникально, когда речь идет о слияниях. Хотя HEAD^обычно это разумное значение по умолчанию.
kdb
2

Если вы хотите заглянуть вперед, вы можете проделать этот трюк, поскольку в Git нет строгой команды для этого.

git log --reverse COMMIT_HASH..

пример

Список хэшей истории логов:

A
B
C -> put this
D

используя команду git log --reverse C.., на выходе вы увидите , B и A .

димпиакс
источник
1

Возможно, не самый лучший способ, но вы можете использовать его git logдля просмотра списка коммитов, а затем использовать git checkout [sha1 of D]для перехода к D.

BB-поколение
источник
6
Я не понимаю, если он в C, то git log покажет ему только C, B и A.
Билтон,
Хорошо, понял, но вам придется сделать хитрый git-log, как указано в ссылке, предоставленной VonC
Bilthon
1

Я только что проверил это. скажем, например, вы находитесь в главной ветке Затем выполните:

git checkout HEAD@{3}

Итак, голова отсоединяется, и вы можете попробовать еще раз, чтобы перейти к любой другой фиксации:

git checkout HEAD@{4}

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

git checkout master

Если вы не хотите переходить в исходное состояние и хотите, чтобы одна из коммитов была вашей головой и продолжала оттуда, вам нужно оттуда разветвляться. например, после "git checkout HEAD @ {4}" вы можете ввести

git checkout -b MyNewBranch
user1322977
источник
0

В качестве обходного пути вы можете просто вернуться к HEAD с помощью

git checkout <branch>

А затем перейдите к желаемой фиксации с помощью

git checkout HEAD~<offset>
Патрицио Бертони
источник
0

Если вы используете vs code, то история Git - отличный плагин, в котором вы можете эффективно видеть коммиты и проверять их содержимое в самом редакторе. проверьте ссылку

Рахиль Ахмад
источник
0
branchName=master; commitInOrder=1; git checkout $(git log --pretty=%H "${branchName}" | tac | head -n "${commitInOrder}" | tail -n 1)

где:

branchName равно имени ветки

commitInOrder равно фиксации в порядке от самой первой фиксации в выбранной ветке (так, 1 - самая первая фиксация, 2 - вторая фиксация в ветке и т. д.)

гость
источник