@gyokuto

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

`git push --force-with-lease` の仕様 (fetchだけでなく、checkout も必要?)

Q&A

git push --force-with-lease の仕様について の疑問です。

manの情報 や ネットの記事だと、
『push対象の remote-trackingブランチ (origin/master とか) が
remoteの状態と一致していること』
が条件と認識していました。

が、どうも、
『一度、該当の remote head に いた履歴があること (checkoutしてること)』
が条件のように思い始めてきました。

こちら、なにか勘違いしているでしょうか?


確認コマンドは、以下です。
確認環境は、Win11上で MSYS2環境 で実施しています。

# 下準備:remoteの bareリポジトリ を作成。
% git init --bare repo.git
Initialized empty Git repository in /PWD/repo.git/
# 下準備:working repo "a" を作成。
% git clone repo.git a
Cloning into 'a'...
warning: You appear to have cloned an empty repository.
done.

% cd a

# rootコミット を作成
% git commit --allow-empty -m "Initial commit"
[master (root-commit) b8c5cd5] Initial commit

% git push origin -u master
Enumerating objects: 2, done.
Counting objects: 100% (2/2), done.
Writing objects: 100% (2/2), 164 bytes | 164.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To /PWD/repo.git
 * [new branch]      master -> master
branch 'master' set up to track 'origin/master'.

% git log --graph --decorate --oneline --branches --remotes @
* b8c5cd5 (HEAD -> master, origin/master) Initial commit
# 下準備:working repo "b" を作成。
% cd ..
% git clone repo.git b
Cloning into 'b'...
done.

% cd b
% git log --graph --decorate --oneline --branches --remotes @
* b8c5cd5 (HEAD -> master, origin/master, origin/HEAD) Initial commit
# "a"側で remote を更新。
% cd ../a
% git commit --allow-empty -m a
[master bd46913] a

% git push origin master
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 176 bytes | 176.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To /PWD/repo.git
   b8c5cd5..bd46913  master -> master

% git log --graph --decorate --oneline --branches --remotes @
* bd46913 (HEAD -> master, origin/master) a
* b8c5cd5 Initial commit
# "b"側で remote を更新。
% cd ../b
% git log --graph --decorate --oneline --branches --remotes @
* b8c5cd5 (HEAD -> master, origin/master, origin/HEAD) Initial commit

% git commit --allow-empty -m b
[master 43301db] b

% git log --graph --decorate --oneline --branches --remotes @
* 43301db (HEAD -> master) b
* b8c5cd5 (origin/master, origin/HEAD) Initial commit
# この時点だと、"a"側の更新 は fetchしていない。

# "a"側更新 がないので、`git push --force-with-lease` は 失敗。
% git push --force-with-lease origin master
To /PWD/repo.git
 ! [rejected]        master -> master (stale info)
error: failed to push some refs to '/PWD/repo.git'

# ここで remoteにある "a"側の更新 を fetch.
% git remote update
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (1/1), 156 bytes | 39.00 KiB/s, done.
From /PWD/repo
   b8c5cd5..bd46913  master     -> origin/master

# 確かに `origin/master` が変更されていること を確認。
% git log --graph --decorate --oneline --branches --remotes @
* 43301db (HEAD -> master) b
| * bd46913 (origin/master, origin/HEAD) a
|/  
* b8c5cd5 Initial commit

# ここで、`origin/master` は 最新のものに fetchしているので、
# `git push --force-with-lease` 成功するもの と思ったが、NG. ★1
% git push --force-with-lease origin master
To /PWD/repo.git
 ! [rejected]        master -> master (remote ref updated since checkout)
error: failed to push some refs to '/PWD/repo.git'
hint: Updates were rejected because the tip of the remote-tracking branch has
hint: been updated since the last checkout. If you want to integrate the
hint: remote changes, use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

# 意図的に、`origin/master` に 移動したうえで、
# 元のコミットに戻って `git push --force-with-lease` し直すと、成功。★2
% git reset origin/master
% git reset @{1}
% git push --force-with-lease origin master
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 176 bytes | 176.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To /PWD/repo.git
 + bd46913...43301db master -> master (forced update)

% git log --graph --decorate --oneline --branches --remotes @
* 43301db (HEAD -> master, origin/master, origin/HEAD) b
* b8c5cd5 Initial commit

上記 ★1 の箇所で、ネット上でよく見る説明だと、
git push --force-with-lease 成功するはず、と思っています
(git remote updateorigin/master が 最新状態になっているので)。

が、実際には、★2 のように、
fetchした後、一度 そこに移動し直した履歴 がないと、成功しません。

なにか 理解が間違っているでしょうか?

それとも、どこかの版で --force-with-lease の仕様が変わった、とかでしょうか?
(man説明だと、--force-with-lease=<refname>:<expect> 以外の形式は experimentalで、
仕様とか変わるかも、とのことですし。)
「fetchするだけで 無意味化するから ちょっと危ない」とは言われていましたし。

直接 git rebase 等で fetchした履歴を取り込んでいるケース で、
「履歴 取り込んでるのに なんでじゃ…?🤔」と悩むことになりました。


なお、--force-with-lease=<refname>:<expect> の形式で、
git push --force-with-lease=master:origin/master と指定すると、
fetch直後に 期待どおりに push成功します。


# 確認ver.
% git --version
git version 2.51.1

% pacman -Q msys2-runtime git
msys2-runtime 3.6.5-1
git 2.51.1-1
1 likes

No Answers yet.

Your answer might help someone💌