Можно ли сбросить заголовок ветви, находясь в другой ветви, т.е. без использования git checkout? - PullRequest
0 голосов
/ 13 октября 2019

предположим, у меня есть две ветви, A и B, и 3 коммита C, D и E. C является родителем D и E, и я на ветке B, которая находится на коммите E. ветвь A на коммите CМожно ли, не проверяя ветку A, сбросить ветку A для фиксации D? Сценарий использования - это то, что я хотел бы получить Git, а затем обновить Develop или Master до Origin / Develop или Master. Возможно ли это в идеале в одной команде? Я видел git branch -f и, насколько я понимаю, выполнение git branch -f AD может делать то, что я хочу, но я недостаточно разбираюсь во внутренних командах git, но не уверен, что это то, что я хочу делать,Таким образом, для тех, кто имеет больше опыта работы с git, достигается ли это желаемый эффект? Может ли это иметь непреднамеренные побочные эффекты? И есть ли лучший способ? Тот, который может быть безопаснее? Кроме того, мне нужно обратиться к коммиту, в который я хочу сбросить, как к хешу, или я могу также использовать ссылки (так, например, просто сделайте git branch -f master origin / master)? редактировать: забыл упомянуть, на какую ветвь А указывает

1 Ответ

3 голосов
/ 13 октября 2019

TL; DR

Да, git branch -f может делать то, что вы хотите. Осторожно, однако, что это будет делать то, что вы говорите, даже если это, возможно, не то, что вы хотели в конце концов. Попробуйте вместо этого использовать слегка магический git push .. Еще лучше, не делайте этого вообще - см. Ниже.

Long

предположим, что у меня есть две ветви, A и B, и 3 фиксации C, D и E. Cродитель D и E, и я на ветви B, которая находится на коммите E.

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

  D
 /
C
 \
  E   <-- branch-B

Вы не сказали нам, какое из трех коммитов branch-A имен / точек, так что я не знаю, где поставить метку branch-A на этом чертеже.

Можно ли без проверки на ветке A сбросить ветку A для принятия D?

Да. Поскольку вы упомянули «без проверки branch-A», мы можем предположить, что вы в данный момент используете branch-B (поскольку branch-A и branch-B являются только ветвями в этом хранилище). Мы также можем предположить, что имя branch-A должно указывать на коммит C сейчас, поэтому нарисуем , что:

  D
 /
C   <-- branch-A
 \
  E   <-- branch-B (HEAD)

Откуда вы знаете хеш-идентификатор фиксации коммита D на первом месте? Там нет имен, по которым его можно найти. Но все же, пока вы знаете знаете это:

Я видел git branch -f, и я, насколько я понимаю, выполнение git branch -f branch-A D может делать то, что я хочу .. .

Это именно то, что он делает. (Существует предостережение, к которому мы вскоре доберемся, но это - это то, что он делает.) Учитывая хэш-идентификатор commit D, git branch -f branch-A заставляет имя branch-A бытьуказывая там, независимо от того, куда он указывал ранее:

  D   <-- branch-A
 /
C
 \
  E   <-- branch-B (HEAD)

Обратите внимание, что ни один коммит никогда не изменяется. Ветвь names перемещается, но график фиксации остается прежним (даже если вам нужно изменить способ его рисования, чтобы сжать имена в :-)).

После того, как вы нарисовали коммиты - используя их настоящие хэш-идентификаторы, или эти замещающие однобуквенные имена, как я, - они останутся такими же навсегда, включая ссылки в обратном направлении от более позднего коммита, например D илиE, его непосредственному родителю (C в данном случае). Однако, если вы уберете все имена из некоторого коммита - как это было в нашем рисунке с коммитом D ранее - эти коммиты станут очень трудно найти. В конце концов - иногда через 14 или 30 дней, как правило, с подробностями, зависящими от настроек и записей reflog, и т.п. - Git действительно действительно удалит все коммиты, которые вы не можете найти, в следующий раз, когда git gc запустится.

Остерегайтесь проблемы XY

Мы только что решили некоторую проблему Y, которую вы планируете использовать для решения несколько иной проблемы X. Давайте рассмотрим проблему X и представим оговорку, о которой я упоминал.

Вариант использования: я хочу выполнить git fetch, а затем обновить ... master до [origin/master].

Итак, предположительно, branch-Aфактически был заменой для master, а коммит D был указан origin/master. Давайте нарисуем , что case:

  D   <-- origin/master (after `git fetch`)
 /
C   <-- master
 \
  E   <-- branch-B (HEAD)

Здесь вы можете запустить git branch -f master origin/master и получить:

  D   <-- master, origin/master
 /
C
 \
  E   <-- branch-B (HEAD)

Обратите внимание, что коммит C остается достижимым: master теперь указывает на фиксацию D, но коммит D указывает на фиксацию C, которая является корневым коммитом графа. (У нас нет коммитов A и B, но если бы мы сделали, предположительно, они пришли бы до C и, следовательно, все еще были бы достижимы.)

Но предположим, что ваши master указывают на новый коммит G, и фактическая картина такова:

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

Команда git branch -f master origin/master будет неподвижным сделать master точным указанием для фиксации D:

  D   <-- master, origin/master
 /
C--F--G
 \
  E   <-- branch-B (HEAD)

Commit G больше не может его найти! git log --all не покажет. В конце концов, через 30 дней или около того, вероятно, git gc удалит его по-настоящему вместе с коммитом F, который также не может быть найден.

Для предотвращения мпереместив master с G назад на C и затем снова на D, вы, вероятно, захотите либо выполнить реальное слияние, выполнив git checkout master, а затем git merge origin/master:

  D__  <-- origin/master
 /   `--.
C--F--G--H   <-- master (HEAD)
 \
  E   <-- branch-B

или, возможно, перебазирование, выполнив git checkout master && git rebase origin/master:

    F'-G'  <-- master (HEAD)
   /
  D   <-- origin/master
 /
C--F--G
 \
  E   <-- branch-B

Перебазировка также теряет F и G, но только после копирования их в новые и улучшенные F'и G', так что все в порядке, чтобы их потерять.

Если вы захотите найти их снова, в течение ~ 30 или более дней, в течение которых они задерживаются, они будут в выводе git reflog master.

Работа с проблемой X

Давайте назовем задачу X «поддержание master в актуальном состоянии, без потери коммитов».

Один из способов справиться с этим - просто git checkout master изатем запустите merge или rebase в зависимости от ситуации. Мне нравится использовать git merge --ff-only, который обновляет master в режиме ускоренной перемотки вперед, перемещая его для фиксации D тогда и только тогда, когда это можно сделать как ускоренную перемотку вперед, а не слияние-все операции. Если нет, я переоцениваю свои коммиты F и G. Должны ли они все-таки быть на ветке, чтобы я смог перемотать вперед master? Должен ли я их перебазировать? Что должен делать с ними?

Если это нарушит мое текущее дерево работы, я могу использовать git worktree add для выполнения этой работы. Добавленное рабочее дерево может быть на ветке master, и тогда я могу делать там все, что мне нужно.

Помимо выполнения git checkout master, однако, есть еще несколько способов решения этой проблемы:

  • Самое простое это , просто полностью удалить имя master. Зачем вам это нужно? Вы начинаете с:

    C   <-- origin/master
     \
      E   <-- branch-B (HEAD)
    

    Затем запускаете git fetch и получаете:

      D   <-- origin/master
     /
    C
     \
      E   <-- branch-B
    

    Вам вообще не нужен ваш master.

  • Но если вам нравится иметь master (по любой причине), вы можете использовать:

    git push . origin/master:master
    

    (Или:

    git fetch . origin/master:master
    

    Я нена самом деле они не используются, но если бы я это сделал, я бы предпочел вариант push. Давайте рассмотрим только вариант push.)

    Имя . действует как URL файла для«Другой» Git ваш Git будет вызывать здесь. Этот другой Git, ну, сам по себе! Так что ваш Git вызывает себя и просит или говорит ему что-то сделать. Оставшийся аргумент - это origin/master:master - говорит вашему Git, что сказать вашему Git:

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

    Поскольку ваш Git говорит сам с собой, то, что он действительно делает, это использует тест «это операция ускоренной перемотки вперед», как это делал бы git merge --ff-only. Ваше имя master переместится в ускоренном порядке, чтобы соответствовать вашему имени origin/master. Если это невозможно - например, если существует коммит F - ваш Git отклонит свой собственный push-запрос.

    Вы можете только использовать это в ветке, которую вы делаетеnot извлекли. 1 Если у вас есть разветвленная ветвь, Git хочет, чтобы вы вместо этого использовали git merge --ff-only, чтобы ваш индекс и рабочее дерево обновлялись правильно.


1 В том, что представляет собой довольно серьезную ошибку, тест Git "проверена ли эта ветвь" проверяет только первичное рабочее дерево,без проверки каких-либо добавленных рабочих деревьев. Если вы используете git worktree add, будьте осторожны!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...