как вы продвигаете только некоторые из ваших локальных коммитов git?

160

Предположим, у меня есть 5 локальных коммитов. Я хочу подтолкнуть только 2 из них к централизованному репо (используя рабочий процесс в стиле SVN). Как мне это сделать?

Это не сработало:

git checkout HEAD~3  #set head to three commits ago
git push #attempt push from that head

Это заканчивает тем, что подталкивает все 5 местных коммитов.

Я полагаю, что я мог бы сделать git reset, чтобы фактически отменить мои коммиты, затем git stash и затем git push - но у меня уже есть записанные сообщения коммитов и организованные файлы, и я не хочу их переделывать.

Я чувствую, что какой-то флаг, переданный для нажатия или сброса, будет работать.

Если это поможет, вот мой конфиг

[ramanujan:~/myrepo/.git]$cat config 
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = ssh://server/git/myrepo.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
рамануджанова
источник

Ответы:

192

Предполагая, что ваши коммиты находятся в главной ветви, и вы хотите перенести их в удаленную главную ветку:

$ git push origin master~3:master

Если вы использовали git-svn:

$ git svn dcommit master~3

В случае с git-svn вы также можете использовать HEAD ~ 3, так как он ожидает фиксации. В случае прямого git вам нужно использовать имя ветки, потому что HEAD не правильно оценивается в refspec.

Вы также можете использовать более длительный подход:

$ git checkout -b tocommit HEAD~3
$ git push origin tocommit:master

Если вы привыкли к такому типу рабочих процессов, вам следует рассмотреть возможность выполнения своей работы в отдельной ветке. Тогда вы можете сделать что-то вроде:

$ git checkout master
$ git merge working~3
$ git push origin master:master

Обратите внимание, что часть «origin master: master», вероятно, не является обязательной для вашей установки.

Райан Грэм
источник
14
Примечание: вам не нужно использовать master~3. Любая ссылка на желаемую «до» фиксацию в равной степени действительна, например, HEAD~3или HEAD~~~, или конкретный SHA, или тег, который маркирует эту фиксацию.
Каз
2
Хорошая вещь. Предупреждение: эти примеры подталкивают к оригиналу master. Если вы копируете и вставляете это решение, вы можете случайно обновить основную ветку. (Конечно, вы всегда должны быть осторожны и перепроверить свою команду перед выдачей git push...)
nofinator
Похоже, что это подталкивает коммит, но не добавляет ветку удаленно.
Nateowami
@Nateowami, для этого вам нужно будет указать что-то иное, чем masterдля удаленной стороны refspec, напримерgit push origin tocommit:newbramch
Райана Грэма
Понимаю. Название филиала уже существовало локально; Я полагаю, это не понравилось. У пульта еще не было названия филиала.
Nateowami
16

Что я делаю, так это работаю в местном отделении под названием «работа». Эта ветвь содержит все временные коммиты (такие как обходные пути или опции частной сборки или что-то еще), которые я не собираюсь выдвигать в обратный репозиторий. Я работаю далеко на этой ветке, а затем , когда я хочу , чтобы совершить переключаюсь на главной ветви, вишня подобрать соответствующие коммиты , что я действительно хочу , чтобы совершить, а затем нажмите мастер.

Вытащив изменения из апстрима в мою главную ветку, я git checkout workи git rebase master. Это переписывает все мои локальные изменения, чтобы быть в конце истории.

Я на самом деле использую git svnс этим рабочим процессом, так что моя операция "толкать" включает в себя git svn dcommit. Я также использую tigхороший просмотрщик репозиториев в текстовом режиме для выбора подходящих коммитов.

Грег Хьюгилл
источник
с помощью git svn dcommit вы можете указать коммит до dcommit до, так что желаемый эффект довольно тривиален с git-svn.
Райан Грэм
У этого подхода есть недостатки (кратко изложено здесь stackoverflow.com/a/881014/1116674 ). Хорошей альтернативой является создание веток для каждой функции, над которой вы работаете, и workветки. Затем вы объединяете определенные ветки, masterчтобы не потерять их историю. Работая с work, вы объединяете все свои ветви в него. Это больше накладных расходов, но в некоторых случаях может стоить того.
Гудон
16

По умолчанию git-push помещает все ветви. Когда вы делаете это:

 git checkout HEAD~3  #set head to three commits ago
 git push #attempt push from that head

Вы переходите к отдельному HEAD (вы не находитесь ни в одной ветви), а затем проталкиваете все ветви, включая локальный мастер (который все еще там, где он был), к удаленному мастеру.

Ручное решение:

 git push origin HEAD:master

Если вы обнаружите, что поведение по умолчанию для всех веток сбивает с толку (и опасно!), Добавьте это в ваш ~ / .gitconfig:

 [remote.origin]
    push = HEAD

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

 error: unable to push to unqualified destination: HEAD
Томас Леонард
источник
10

Короткий ответ:

git push <latest commit SHA1 until you want commits to be pushed>

Примеры:

git push fc47b2

git push HEAD~2

Длинный ответ:

Коммиты связаны друг с другом в виде цепочки с родительским / дочерним механизмом. Таким образом, отправка коммита фактически также подталкивает все родительские коммиты к этому коммиту, которые неизвестны удаленному. Это неявно делается, когда вы делаете git pushтекущий коммит: все предыдущие коммиты также передаются, потому что эта команда эквивалентна git push HEAD.

Таким образом, вопрос может быть переписан в Как нажать конкретный коммит, и этот конкретный коммит может быть, например, HEAD ~ 2.

Если коммиты, которые вы хотите выдвинуть, не являются последовательными, просто измените их порядок git rebase -iперед определенным толчком .

Тим
источник
5

1) Используйте «git rebase», чтобы изменить порядок ваших коммитов, если хотите.

git rebase -i

Эта команда отобразит что-то вроде этого в вашем редакторе (я использую vim)

pick 4791291 commitA
pick a2bdfbd commitB
pick c3d4961 commitC
pick aa1cefc commitD
pick 9781434 commitE

# Rebase ..............
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out




^G Get Help         ^O WriteOut         ^R Read File        ^Y Prev Page                ^K Cut Text         ^C Cur Pos
^X Exit             ^J Justify          ^W Where Is         ^V Next Page            ^U UnCut Text       ^T To Spell

2) Упорядочить ваши коммиты по вашему выбору с помощью простой резки пасты. Предположим, новый заказ

выбрать 9781434 коммит

выбрать c3d4961 commitC

выбрать 4791291 commitA

выберите aa1cefc commitD

выберите a2bdfbd commitB

Сделайте эти изменения в вашем редакторе и нажмите Ctrl + O (writeOut)

Или вы также можете использовать

git rebase -i HEAD~<commitNumber>

Вы можете проверить новую последовательность с

git log

3) Теперь используйте

git push <remoteName> <commit SHA>:<remoteBranchName>

Если только одна ветвь в удаленной (источник) и одна в локальной (мастер), просто используйте

git push <commit SHA>
git push aa1cefc

Это подтолкнет commitB и commitD.

Йогеш Ядав
источник