У меня простая настройка, в которой мой локальный репозиторий имеет одну ветку main
.
Эта ветка main
настроена для:
main
в моем общедоступном репозитории GitHub.main
моего частного репозитория GitHub.Почему?
Эта установка не позволяет мне:
По какой-то причине при такой настройке git pull --rebase
всегда переопределяет мои локальные коммиты теми, которые были взяты из личного общедоступного репозитория. Он правильно fetch
фиксирует изменения в локальной ветке удаленного отслеживания, но на самом деле не перебазирует соответствующую ветку. Это просто переопределяет это.
Я хотел бы знать, это git
ошибка или я делаю что-то не так?
Мне удалось воспроизвести проблему с локальными репозиториями, выполнив следующие действия:
mkdir /tmp/repo_1 /tmp/repo_2
# Initialize first repository with a single commit that creates file "a".
git -C /tmp/repo_1 init --quiet
touch /tmp/repo_1/a
git -C /tmp/repo_1 add a
git -C /tmp/repo_1 commit --quiet --message "create file a"
# Initialize second repository which will be used for pushing.
git -C /tmp/repo_2 init --bare --quiet
# Initialize local repository and configure it to pull from "repo_1" and push to "repo_2"
git clone /tmp/repo_1 /tmp/local --quiet
git -C /tmp/local remote set-url --push origin /tmp/repo_2
# Make second commit in a local repository (first one was cloned from "repo_1") that creates file "b".
touch /tmp/local/b
git -C /tmp/local add b
git -C /tmp/local commit --quiet --message "create file b"
# Push both commits to "repo_2".
git -C /tmp/local push --quiet
# Pull --rebase changes from "repo_1" and notice that second commit is now missing.
echo "Local commits before pull --rebase:"
git -C /tmp/local log --oneline
echo
git -C /tmp/local pull --rebase --quiet
echo "Local commits after pull --rebase:"
git -C /tmp/local log --oneline
Обратите внимание, что замена последнего абзаца ручными командами fetch
+ rebase
дает ожидаемый результат:
# Do manual fetch + rebase and notice that it works as expected.
echo "Local commits before fetch + rebase:"
git -C /tmp/local log --oneline
echo
git -C /tmp/local fetch --quiet
git -C /tmp/local rebase --quiet origin/master
echo "Local commits after fetch + rebase:"
git -C /tmp/local log --oneline
Git-версия: 2.45.2
Я решил написать более прямой ответ вместо того, чтобы просто указывать на ссылки в моем комментарии выше.
# Push both commits to "repo_2". git -C /tmp/local push --quiet
Когда OP неявно нажимает на repo_2, они продвигают удаленную ветку «main» на repo_2 и локальную ветку удаленного отслеживания «origin/main». По замыслу «repo_1» не получает коммитов, а удаленная ветка «main» в repo_1 остается прежней.
$ git -C /tmp/repo_1 log --oneline
2b75abd (HEAD -> main) create file a
$ git -C /tmp/repo_2 log --oneline
f74a217 (HEAD -> main) create file b
2b75abd create file a
$ git -C /tmp/local log --oneline
f74a217 (HEAD -> main, origin/main, origin/HEAD) create file b
2b75abd create file a
На этом этапе, если бы мы выполнили «git fetch» (неявно из repo_1), мы увидели бы сообщение о принудительном обновлении в ветке удаленного отслеживания «origin/main», поскольку оно будет принудительно возвращено к исходному коммиту ( сообщение фиксации «создать файл a»). Однако ручное перебазирование локальной «main» на удаленную ветку отслеживания «origin/main» на этом этапе дало бы желаемый эффект. Однако вместо ручной выборки и перебазирования предположим, что мы используем «git pull --rebase».
# Pull --rebase changes from "repo_1" and notice that second commit is now missing.
'git pull --rebase' имеет специальную логику, предназначенную для распознавания перезаписи удаленной ветки (например, перебазирования). Это упоминается в опции «-rebase» справки «git pull»:
Если существует ветвь удаленного отслеживания, соответствующая восходящему ветвь и восходящая ветвь были перебазированы с момента последней выборки, rebase использует эту информацию, чтобы избежать переноса нелокальных изменений.
В этом случае специальная логика срабатывает из-за того, что удаленная ветка «main» в repo_1 («создать файл a»), которая обычно синхронизируется с веткой удаленного отслеживания или опережает ее, вместо этого находится позади удаленной ветки отслеживания. ветка «origin/main» («создать файл b»).
Эта специальная логика пытается идентифицировать «локально созданные» коммиты и старые коммиты, которые, по-видимому, раньше находились на удаленном компьютере, но были заменены перезаписью/перебазированием. Он делает это, просматривая ветку удаленного отслеживания «origin/main», которая указывает на фиксацию «создать файл b», и после этого не видит никаких коммитов. Похоже, это указывает на то, что удаленная ветка «main» в repo_1 была намеренно перебазирована/переписана, чтобы удалить фиксацию «создать файл b», поэтому origin/main сбрасывается для фиксации «создать файл a». Поскольку логика не выявила локальных коммитов для перебазирования, «main» также сбрасывается на «origin/main».
$ git -C /tmp/repo_1 log --oneline
2b75abd (HEAD -> main) create file a
$ git -C /tmp/local log --oneline
f74a217 (HEAD -> main, origin/main, origin/HEAD) create file b
2b75abd create file a
$ git -C /tmp/local pull
From /tmp/repo_1
+ f74a217...2b75abd main -> origin/main (forced update)
Successfully rebased and updated refs/heads/main.
$ git -C /tmp/local log --oneline
2b75abd (HEAD -> main, origin/main, origin/HEAD) create file a
Я думаю, что обычный вариант использования для указания другого удаленного push-управления заключается в том, что при извлечении и отправке с удаленного устройства необходимо использовать разные протоколы для доступа к одному и тому же репо. Я не уверен, есть ли какие-либо другие ошибки в git, которые могут появиться, когда URL-адреса для извлечения и отправки фактически указывают на разные репозитории, синхронизация которых не гарантируется.
Другой способ выполнить то, что делает OP, — использовать два отдельных пульта для repo_1 и repo_2, а затем отключить отправку в repo_1 либо на сервере, либо на клиенте (возможно, установив для push-URL для repo_1 фиктивное значение). Тогда 'git log --all --decorate' будет точно отражать состояние каждого пульта. Затем можно всегда извлечь данные из обоих репозиториев (git fetch --all --prune), а затем убедиться, что локальные ветки созданы и связаны с удаленными ветвями отслеживания для repo_2, например. git checkout --track repo_1/feature; Функция git push -u repo2. Затем можно было бы извлечь коммиты из веток repo_1 в локальную ветку, например. 'git merge --ff-only repo_1/main'.
Больше информации:
Блин, очень хорошее объяснение и ссылки на ссылки, спасибо! Я знаю об удаленной настройке origin
/upstream
, но при этом приходится постоянно их указывать. Когда я узнал, что могу предотвратить отправку в репозиторий компании, как вы также предложили, установив URL-адрес push-уведомления на какую-то ерунду, я подумал Why not just specify explicit push and pull for "same" origin?
, и вот мы здесь :D Думаю, я пока пойду вручную fetch
+ rebase
, если только я не столкнетесь с новыми неприятностями на пути.
См. stackoverflow.com/a/11531552/11296 («git pull --rebase — это НЕ то же самое, что git fetch; git rebase») и stackoverflow.com/questions/6284887/… («Однако, если upstream rebase случайно удалил коммиты, тогда магия молча не позволит вам вернуть их обратно. В этом случае, если вы хотите вернуть эти удаленные коммиты, вам следует вместо этого использовать git fetch; git rebase." - ваш вариант использования выглядит именно так. ) и gitolite.com/git-pull--rebase.html