git push — 手元のコミットを GitHub に送る
コミットは、作った時点では手元のパソコンの中にしかありません。それを GitHub に送るのが git push です。一言でいうと、手元のコミットを GitHub に送り、GitHub 側のブランチの付箋を進めるコマンドです。この回では、push で動く2枚の札と出力の読み方、新しいブランチを送る push -u、もう1台の PC が先に push していると拒否される場面を見ます。
- 上のターミナルは、実際に打ったときと同じ出力です(Git は英語で答えます)
- 下の中で起きていることには、グラフが2つ並びます。上が GitHub 側、下が手元で、同じコミットは上下で同じ横位置に来ます。丸がコミット、黒い札がブランチ、ピンクの札が HEAD です
- 手元の点線の札
origin/mainは、GitHub のmainの位置を手元に写した札です(2-1 で見た追跡ブランチ)。GitHub 側の札が動いても、点線の札は push や fetch をするまで動きません - 右の説明をスクロールして読み進めると、左のターミナルでコマンドが1つずつ打たれます。
→キーでも1つずつ進みます(←で1つ戻ります。スマホでは「次へ」ボタン)。最後まで進んでから→を押すと次の区間へ移ります。黄色く光っているのが、そのコマンドで変わった場所です
1. git push — 送ると、2枚の札が動く
2-1 origin と origin/main の正体で見たとおり、clone したリポジトリには origin(GitHub の住所に付けた呼び名)があり、手元には追跡ブランチ origin/main(最後に確かめたときの GitHub の main の位置)が貼ってあります。git push がすることは3つです。
- GitHub に無いコミットを、手元から GitHub に送る
- GitHub 側のブランチの付箋を、手元のブランチと同じコミットまで進める
- 手元の
origin/mainも同じ位置へ進める
手元の main は、もともと送ったコミットに貼ってあるので動きません。push で動くのは、GitHub 側の main と手元の origin/main の2枚だけです。
git push — コミットを送ると2枚の札が動く
出発点です。上の GitHub 側の
mainも、下の手元のmainも「卵を追加」に貼ってあります。手元の点線の札origin/mainは、GitHub のmainの位置を手元に写した 追跡ブランチ です。todo.txtに「パンを買う」を書き足して、コミットします。$ echo "パンを買う" >> todo.txt進んだのは手元の
mainだけです。GitHub 側の図は何も変わっていません。コミットは、push するまで手元のパソコンの中にしかありません。$ git commit -am "パンを追加" [main c43e6a8] パンを追加 1 file changed, 1 insertion(+)ahead of 'origin/main' by 1 commitは「GitHub の写し(origin/main)より1つ先に進んでいる」という意味です。次の行で Git が、送るならgit pushだと教えています。$ git status On branch main Your branch is ahead of 'origin/main' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean(HEAD -> main)と(origin/main, origin/HEAD)が別の行に付いています(origin/HEADは GitHub の既定のブランチを指す写しで、2-1 で見ました)。origin/mainの行より上にあるコミットが、push で送られるものです。$ git log --oneline --all c43e6a8 (HEAD -> main) パンを追加 8b41d07 (origin/main, origin/HEAD) 卵を追加 3f2a9c1 牛乳を追加git pushは手元のコミットを GitHub に送り、GitHub 側のmainと手元のorigin/mainを一緒に進めます(手元のmainは動きません)。8b41d07..c43e6a8 main -> mainは「手元のmainを GitHub のmainへ送り、8b41d07からc43e6a8まで進めた」と読みます。$ git push To https://github.com/you/memo-app.git 8b41d07..c43e6a8 main -> mainup to date with 'origin/main'に戻り、ahead の行が消えました。手元のmainと GitHub の写しが同じコミットにある、という意味です。$ git status On branch main Your branch is up to date with 'origin/main'. nothing to commit, working tree clean送るものが無いときは
Everything up-to-dateと出て、何も変わりません。何度打っても害はありません。$ git push Everything up-to-dateもう1行書き足して、コミットします。
$ echo "バターを買う" >> todo.txtまた手元の
mainだけが1つ先に進みました。$ git commit -am "バターを追加" [main 492da8b] バターを追加 1 file changed, 1 insertion(+)-q(quiet=静かに)を付けると、うまくいったときは何も表示しません。中でしていることはgit pushと同じで、2枚の札が一緒に進みました。Claude Code はこの形をよく打ちます。$ git push -q(HEAD -> main, origin/main, origin/HEAD)と同じ行に並びました。出力の無い push が届いたかどうかは、ここで確かめられます。$ git log --oneline -3 492da8b (HEAD -> main, origin/main, origin/HEAD) バターを追加 c43e6a8 パンを追加 8b41d07 卵を追加
push で送られるのはコミットだけです。コミットしていない書きかけは送られません。GitHub に変更が出ていないときは、まずコミットしたかを git status で確かめます。
本物の git push をターミナルで打つと、To https://… の前に Enumerating objects: 5, done. のような進み具合の行も出ますが、この画面では省いています。
2. push の出力と status の読み方
1節の push の出力を、部分ごとに読みます。
| 出力 | 意味 |
|---|---|
To https://github.com/you/memo-app.git | 送り先。origin という呼び名の中身 |
8b41d07..c43e6a8 | GitHub 側のブランチを 8b41d07 から c43e6a8 まで進めた |
main -> main | 左が手元のブランチ、右が GitHub 側のブランチ |
Everything up-to-date | 送るものが無かった。何も変わっていない |
8b41d07..c43e6a8 の左側は、push する前に GitHub 側の main が指していたコミットです。push の前に git log --oneline --all で見た origin/main の付いた行の ID と同じなら、思ったとおりの位置から進めたことになります。
コミットすると git status に Your branch is ahead of 'origin/main' by 1 commit. と出ます。ahead は「手元の main が写しより1つ先にある=まだ送っていないコミットが1つある」という意味で、push すると消えて up to date with 'origin/main' に戻ります。気をつけたいのは、比べている相手が手元にある写しだということです。git status は GitHub に見に行かないので、up to date は「最後に確かめたときの GitHub と同じ」という意味にすぎません。このずれが問題になる場面を4節で見ます。
Claude Code がよく打つ git push -q の -q は quiet(静かに)の略で、うまくいったときは何も表示しません。送るものも、動く札も git push と同じです。届いたかを確かめたいときは、git log --oneline -3 で (HEAD -> main, origin/main, origin/HEAD) が同じ行に並んでいるかを見ます(origin/HEAD は 2-1 で見た、GitHub の既定のブランチを指す写し)。
3. 新しいブランチの push — 上流と git push -u
作業用のブランチを手元で作って push しようとすると、git push だけでは止まります。原因は上流(upstream)が無いことです。上流は、手元のブランチごとに決めておく「push や pull の相手になる GitHub 側のブランチ」です。clone したときの main には最初から上流 origin/main が付いていますが、git switch -c memo で作ったブランチには付いていません。送り先が決まっていないので、Git は勝手に送らずに止まります。
新しいブランチの push — 上流と push -u
mainから作業用のブランチmemoを作って乗り換えます。この付箋は手元にしかなく、GitHub 側にはまだmemoがありません。$ git switch -c memo Switched to a new branch 'memo'メモ用の新しいファイル
notes.txtを作ります。$ echo "牛乳は低脂肪" > notes.txtステージに写します。
$ git add notes.txt手元の
memoが1つ進みました。このコミットは、まだ手元にしかありません。$ git commit -m "メモを追加" [memo ad1a0b0] メモを追加 1 file changed, 1 insertion(+) create mode 100644 notes.txtmainの行には[origin/main]がありますが、memoの行には角かっこがありません。memoにはまだ 上流(push や pull の相手になる GitHub 側のブランチ)が決まっていない、という意味です。$ git branch -vv main e5c0a62 [origin/main] パンを追加 * memo ad1a0b0 メモを追加上流が無いので、
git pushだけでは送り先が決まらず止まります。Git はgit push --set-upstream origin memoを打てば送れる、と教えています。GitHub 側の図は何も変わっていません。$ git push fatal: The current branch memo has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream origin memo To have this happen automatically for branches without a tracking upstream, see 'push.autoSetupRemote' in 'git help config'.-uは--set-upstreamの短い書き方です。GitHub 側にmemoができ(* [new branch])、手元にorigin/memoの札が貼られ、branch 'memo' set up to track 'origin/memo'.で上流が決まりました。remote:で始まる行は GitHub からの返事で、プルリクエストを作る画面の URL です(2-4 で扱います)。$ git push -u origin memo remote: remote: Create a pull request for 'memo' on GitHub by visiting: remote: https://github.com/you/memo-app/pull/new/memo remote: To https://github.com/you/memo-app.git * [new branch] memo -> memo branch 'memo' set up to track 'origin/memo'.memoの行に[origin/memo]が付きました。上流の設定は手元に残るので、-uは最初の1回だけで足ります。$ git branch -vv main e5c0a62 [origin/main] パンを追加 * memo ad1a0b0 [origin/memo] メモを追加notes.txtに1行書き足して、コミットします。$ echo "卵は10個入り" >> notes.txt手元の
memoだけが1つ進みました。$ git commit -am "卵のメモを追加" [memo cd23d7e] 卵のメモを追加 1 file changed, 1 insertion(+)上流があるので、
ahead of 'origin/memo' by 1 commitと GitHub の写しとの差が出るようになりました。$ git status On branch memo Your branch is ahead of 'origin/memo' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean今度は
git pushだけで送れます。ad1a0b0..cd23d7e memo -> memoで、GitHub 側のmemoと手元のorigin/memoが一緒に進みました。$ git push To https://github.com/you/memo-app.git ad1a0b0..cd23d7e memo -> memo
止まったときの出力にあった git push --set-upstream origin memo の短い書き方が -u で、git push -u origin memo はGitHub に memo を作ってコミットを送り、手元に origin/memo を貼り、memo の上流を origin/memo に決める、の3つを一度にします。上流の設定は手元に残るので、-u が要るのはそのブランチを最初に送る1回だけです。
| コマンド | GitHub にブランチができる | 上流が付く | 次から git push だけで送れる |
|---|---|---|---|
git push(上流が無いとき) | できない(止まる) | 付かない | 送れない |
git push origin memo | できる | 付かない | 送れない(また止まる) |
git push -u origin memo | できる | 付く | 送れる |
git push だけで送るのはいま乗っているブランチだけです。push -u の出力の remote: で始まる行は GitHub からの返事で、その URL はこのブランチからプルリクエストを作る画面です。プルリクエストは 2-4 で扱います。
4. 先に push されていると拒否される
Mac で push したのを忘れて、Windows のノート PC でもコミットして push する。2台で作業するとよく起きるこの場面で、GitHub は push を拒否します。
push が GitHub 側の付箋を進めるのは、GitHub 側のブランチが指すコミットが、送ったコミットの祖先にあるときだけです。この進め方を fast-forward(早送り)と呼びます。GitHub 側に手元に無いコミットがあるのに付箋を動かすと、そのコミットはどのブランチからも辿れなくなります。それを防ぐために、Git は付箋を進めずに断ります。
先に push されていると拒否される
この Windows のノート PC では、手元の
mainも GitHub のmainも「パンを追加」にあります。同じリポジトリを、もう1台の Mac でも使っているとします。Mac で「バターを追加」をコミットして push しました。GitHub 側の
mainが1つ進みます。こちらのターミナルには何も出ません。up to date with 'origin/main'と出ますが、比べている相手は手元にある GitHub の写しです。git statusは GitHub に見に行かないので、Mac の push にはまだ気づいていません。$ git status On branch main Your branch is up to date with 'origin/main'. nothing to commit, working tree cleanそれを知らずに、こちらでも「ジャムを買う」を書き足してコミットします。
$ echo "ジャムを買う" >> todo.txt手元の
mainが「パンを追加」から1つ進みました。GitHub 側のmainも同じ「パンを追加」から別のコミットへ進んでいるので、歴史が2つに分かれています。$ git commit -am "ジャムを追加" [main bbe8599] ジャムを追加 1 file changed, 1 insertion(+)main -> main (fetch first)で拒否されました。GitHub 側に手元に無いコミットがあり、このままmainを進めると Mac の「バターを追加」が辿れなくなるからです。GitHub 側の図は何も変わっていません。$ git push To https://github.com/you/memo-app.git ! [rejected] main -> main (fetch first) error: failed to push some refs to 'https://github.com/you/memo-app.git' hint: Updates were rejected because the remote contains work that you do not hint: have locally. This is usually caused by another repository pushing to hint: the same ref. If you want to integrate the remote changes, use hint: 'git pull' before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.手元の履歴も何も変わっていません。「ジャムを追加」はそのまま残り、Mac の「バターを追加」はまだ手元に届いていないので出てきません。
$ git log --oneline --all bbe8599 (HEAD -> main) ジャムを追加 e5c0a62 (origin/main, origin/HEAD) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加ahead of 'origin/main' by 1 commitのままです。拒否された push は、手元にも GitHub にも何も書き込みません。$ git status On branch main Your branch is ahead of 'origin/main' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean拒否は「先に GitHub の新しいコミットを取り込んでから送ってほしい」という合図です。取り込み方(
git fetchとgit pull)は、次の 2-3 で扱います。
拒否された push は、手元にも GitHub にも何も書き込みません。理由の (fetch first) は「GitHub 側に、手元がまだ受け取っていないコミットがある。先に取ってきて」という意味で、ヒントが 'git pull' before pushing again(もう一度 push する前に git pull して)と勧めているのも同じことです。先に GitHub の新しいコミットを取り込み、それから送り直すのが正しい順番です。
拒否を力ずくで通す git push --force を打つと、Mac で push した「バターを追加」は GitHub のどのブランチからも辿れなくなります。拒否は「上書きすると誰かのコミットが消える」という知らせなので、まずは取り込む方を選びます。force push とその安全な形 --force-with-lease は、第3部の 3-3 で扱います。
5. Claude Code と push
Claude Code が延べ約14,000回打った Git のコマンドのうち、push は310回でした。多いのは git push -q と git push -u origin ブランチ です。push の前後でよく打つのが git branch -r --contains コミットID です。origin/main などの追跡ブランチのうち、そのコミットを含むものを一覧するコマンドで、「このコミットは GitHub に送ってあるか」を確かめています。答えは「最後に確かめたときの GitHub」についてのものなので、別の PC から push された分まで確かめるには、先に git fetch が要ります。
6. Claude Code の許可画面で見かけたら
第1部のコマンドは、どれも手元のパソコンの中だけで完結していました。push は違います。GitHub 側が変わるので、ほかの人や別の PC から見えるようになります。手元なら reflog で取り戻せたものも、一度 GitHub に出たものは「出た」という事実が残ります。
| コマンド | 何が起きるか | 消えうるもの |
|---|---|---|
git status / git branch -vv / git branch -r --contains | 見るだけ(手元の写しと比べる) | なし |
git push / git push -q / git push origin main | コミットを GitHub に送り、GitHub 側のブランチと手元の origin/main を進める | 注意 消えるものは無い。GitHub 側が変わり、ほかの人や別の PC から見える。取り消しても跡が外に残る |
git push -u origin ブランチ | GitHub に新しいブランチを作って送り、上流を決める | 注意 消えるものは無い。そのブランチが GitHub 上で見えるようになる |
拒否された push(! [rejected]) | 何も書き込まずに止まる | なし |
git push --force / git push --force-with-lease | 早送りできない場面でも、GitHub 側のブランチを手元の位置へ動かす(--force-with-lease は手元の写しが古ければ止まる。詳しくは 3-3) | GitHub 側にだけあるコミット(ほかの人や別の PC が push したもの) |
見分け方は、--force(-f)や --force-with-lease が付いているかどうかです。付いていない push は早送りでしか付箋を進めないので、誰のコミットも消しません。代わりに気にするのは、送る先が合っているかと送ってよい中身かです。許可する前に git log --oneline origin/main..HEAD(origin/main には無く HEAD にはあるコミットの一覧)で、送られるコミットを確かめると安心です。
まとめ
git pushは、手元のコミットを GitHub に送り、GitHub 側のブランチを進める。同時に手元のorigin/mainも進む。手元のmainは動かない8b41d07..c43e6a8 main -> mainは「手元のmainを GitHub のmainへ送り、8b41d07からc43e6a8まで進めた」。送るものが無ければEverything up-to-dategit statusの ahead は「まだ送っていないコミットの数」。push すると消える。比べる相手は手元の写しで、GitHub には見に行かない- 新しいブランチには上流が無いので
git pushだけでは止まる。最初にgit push -u origin ブランチを1回打てば、次からgit pushだけで送れる - GitHub 側に手元に無いコミットがあると
(fetch first)で拒否される。拒否された push は何も書き込まない。git push -qは出力が消えるだけ
次の回では、拒否されたときに GitHub の新しいコミットを取り込む git fetch と git pull を扱います。fetch は手元の origin/main の札だけを動かし、手元の main と作業ツリーには触りません。