この最新のGitサブツリーをプッシュできないのはなぜですか?


88

それらの間でいくつかの基本コードを共有するために、私が取り組んでいるいくつかのプロジェクトでGitサブツリーを使用しています。基本コードは頻繁に更新され、いずれのプロジェクトでもアップグレードが行われ、最終的にはすべてが更新されます。

サブツリーが最新であるとgitが報告する問題に遭遇しましたが、プッシュは拒否されます。例えば:

#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch            master     -> FETCH_HEAD
Already up-to-date.

プッシュすると、何もプッシュしないというメッセージが表示されます... 正しい?:(

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To git@github.com:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

これの理由は何でしょうか?プッシュが失敗するのはなぜですか?


なぜgitが言っていることをしないのですか:git pull?
AlexWien

8
Gitプルは私の質問とは関係ありません。しかし、はい、元のリポジトリには何も新しいものがないため、git pullを実行すると最新のメッセージが表示されます。
mateusz

1
現在のブランチがマスターHEADがある場所の後ろにあるようです-たとえば、...-> A-> B-> Cの履歴ですが、リモートマスターHEADはCにありますが、現在のブランチはAにあります(または、Cに接続されていないAの子孫)。確かにgitの履歴を見てみます。
マークレイトンフィッシャー

私も同じ問題を抱えています。--rejoinサブツリーのプッシュを高速化するために使用しています。サブツリーのプッシュを手動で実行してを実行subtree split --rejoinすると、通常、サブツリーの履歴全体が含まれている場合、分割されたサブツリーのブランチには最後の再結合までの履歴しかありません。私にとっては、それが早送り以外のエラーの直接の原因です。サブツリーの分割が切り捨てられた履歴を生成する理由はまだわかりません。
急いでmaplelad

回答:


98

私はこのブログのコメントhttps://coderwall.com/p/ssxp5qで答えを見つけました

「現在のブランチの先端が遅れているため、更新が拒否されました。リモートの変更をマージしてください(例: 'git pull')」(何らかの理由により、特にgitの履歴をめちゃくちゃにして)プッシュしているときの問題次に、gitコマンドをネストして、herokuへのプッシュを強制できるようにする必要があります。たとえば、上記の例の場合:

git push heroku `git subtree split --prefix pythonapp master`:master --force

2
助けてくれた!ありがとう。
Michael Forrest 2013年

8
Windowsでこのコマンドを実行する方法は?取得中ですerror: unknown option 'prefix'
Pilau 2013年

3
すばらしい、これは私の日を救った:)
bpipat 14年

2
確かに、この方法でのサブツリーのほとんどの使用法は、herokuへの1つの方法です。一般に、herokuは、複数のユーザーがプッシュおよびプルするための事実上のgitリポジトリとは見なされていません。
Eric Woodruff

11
私はどのような関係herokuを特定pythonappし、その答えに苦労しました。これをHeroku固有の言語から翻訳:git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force
aednichols

38

Windowsでは、ネストされたコマンドは機能しません。

git push heroku `git subtree split --prefix pythonapp master`:master --force

最初にネストされたビットを実行することができます:

git subtree split --prefix pythonapp master

これは(多くの数の後)トークンを返します、例えば

157a66d050d7a6188f243243264c765f18bc85fb956

これを含むコマンドで使用します。例:

git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force

これははるかにクリーンなソリューションです。私のために働く!
インフィニティ

git subtree split --prefix subtreedirectoryname masterを実行すると、致命的なあいまいな引数 'master'が表示されます。作業ツリーにない不明なリビジョンまたはパス
Demodave

これは素晴らしいソリューションgit subtreeです。以前はコマンドを取得していませんでした。あなたのソリューションを使用した後、私のすべてのリポジトリの代わりにフォルダーをherokuにデプロイすることができました
pakman198


29

--ontoフラグを使用:

# DOESN'T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master

[編集:残念ながら、基になるにsubtree push転送しないため、操作は2つのコマンドで実行する必要があります!これで、私のコマンドは他の回答のコマンドと同じであることがわかりますが、説明が異なるため、ここでそのままにしておきます。]--ontosplit

git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master

または、bashを使用していない場合:

git subtree split --prefix=public/shared --onto=project-shared/master
# This will print an ID, say 0123456789abcdef0123456789abcdef,
# which you then use as follows:
git push project-shared 01234567:master

これを把握するためにgit-subtreeソースを何時間も調べたので、感謝します;)

subtree pushを実行することから始めsubtree splitます。これにより、コミット履歴がプッシュの準備ができているはずの形式に書き換えられます。これを行う方法は、public/shared/パスのあるパスの前を取り除き、パスのないファイルに関する情報を削除することです。つまり、つぶされていないものをプルした場合でも、上流のサブリポジトリのコミットはすべて、ファイルに名前を付けているため、無視されます。(下のファイルに触れないコミットpublic/shared/、または親と同一のコミットコミットも折りたたまれます。[編集:また、いくつかのスカッシュ検出を見つけたので、非スカッシュをプルした場合のみであると考えています。さらに別の回答で説明されている単純なマージコミットの折りたたみにより、非スカッシュパスが選択され、押しつぶされたパスを破棄します。])結局のところ、プッシュしようとするものには、プッシュ元の現在のホストリポジトリに誰かがコミットした作業が含まれますが、サブリポジトリに直接または別のホストを介してコミットした作業は含まれません。リポジトリ。

ただし、を使用する--ontoと、上流のすべてのコミットが逐語的に使用できるように記録されます。そのため、書き換えプロセスが、書き換えの対象となるマージの親の1つとしてそれらに遭遇した場合、書き換えを試みるのではなく、それらを保持します。通常の方法で。


2
この回答は、すでに2度役に立った回答としてお勧めします。また、説明は、分割して適切な場所にプッシュする方法を理解するために必要なものです。ありがとうございました。
カミルウォズニアック2016

2
「プロジェクト共有」とは何ですか?
Colin D

1
@ColinD "project-shared"はリモートの名前です。これは、フェッチしてプッシュする場所を構成するときに選択した名前です。でリモコンのリストを取得できますgit remote -v
entheh 2016年

@enthehあなたがこれに費やした時間は、jsutが私の一日を作りました。ありがとう!
2016

1
このバグは、サブリポジトリにプレフィックスと同じ名前のディレクトリがある場合に常に発生することを発見しました。たとえば、サブリポジトリを「libxxx」としてメインリポジトリに追加し、サブリポジトリ自体にも「libxxx」というサブディレクトリがある」この場合、gitはサブリポジトリのコミットをメインリポジトリのコミットとして誤って扱い、それらを分割しようとします。この--ontoオプションは、gitが既にサブリポジトリにあるコミットを特定するのに役立ちます。
Cosyn

13

「GitHubページ」タイプのアプリの場合、「dist」サブツリーをgh-pagesブランチにデプロイすると、ソリューションは次のようになります。

git push origin `git subtree split --prefix dist master`:gh-pages --force

これは、上記のherokuの例とは少し違って見えるので、これについて触れます。私の "dist"フォルダーが私のリポジトリのマスターブランチに存在することがわかります。次に、それをサブツリーとして、同じくoriginにあるgh-pagesブランチにプッシュします。


3

以前にもこの問題に遭遇したことがあり、ここで私がそれを解決した方法を示します。

私が見つけたのは、ローカルのマスターブランチに接続されていないブランチがあったことです。このブランチが存在し、そのボイドにぶら下がっています。あなたの場合、それはおそらく呼び出されproject-sharedます。これが事実であると仮定するとgit branch、ローカルproject-sharedブランチを表示できる場合、次のようにして、既存のブランチに新しいコミットを「追加」できますproject-shared

git subtree split --prefix=public/shared --onto public-shared --branch public-shared

私が理解した方法は、git subtreeから新しいブランチの作成を開始することです。--ontoこの場合はローカルpublic-sharedブランチです。次に、ブランチは、古いpublic-sharedブランチを置き換えるだけのブランチを作成することを意味します。

これにより、public-sharedブランチの以前のSHAがすべて保持されます。最後に、あなたは

git push project-shared project-shared:master

project-sharedリモートも持っていると仮定します。これにより、void project-sharedブランチにぶら下がっているローカルmasterがリモートのブランチにプッシュされますproject-shared


3

これは、元のアルゴリズムの制限によるものです。マージコミットを処理する場合、元のアルゴリズムは単純化された基準を使用して、無関係な親を切り離します。特に、同じツリーを持つ親があるかどうかをチェックします。そのような親が見つかった場合、マージコミットを折りたたみ、代わりに親コミットを使用します。他の親にサブツリーに関係のない変更があると想定します。場合によっては、これにより履歴の一部が削除され、実際にサブツリーが変更されることがあります。特に、サブツリーに接触するコミットのシーケンスをドロップしますが、同じサブツリー値になります。

これがどのように機能するかを理解するための例(簡単に再現できる)を見てみましょう。次の履歴を考えてみます(行の形式は次のとおりです:commit [tree] subject):

% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s"
*   E [z] Merge branch 'master' into side-branch
|\
| * D [z] add dir/file2.txt
* | C [y] Revert "change dir/file1.txt"
* | B [x] change dir/file1.txt
|/
*   A [w] add dir/file1.txt

この例では、で分割していdirます。commit と同じツリーがDありEます。commitがあり、zcommit Cは元に戻されているためB、変更があったB-Cとしてdirもシーケンスは何もしません。

次に分割を行います。まず、コミット時に分割しCます。

% git log `git subtree split -P dir C` ...
* C' [y'] Revert "change dir/file1.txt"
* B' [x'] change dir/file1.txt
* A' [w'] add dir/file1.txt

次に、コミット時に分割しEます。

% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt

はい、2つのコミットが失われました。2番目のスプリットをプッシュしようとすると、すでにオリジンに入っている2つのコミットがないため、エラーが発生します。

通常push --force、ドロップされたコミットには重要な情報が含まれていないため、を使用してこのエラーを許容できます。長期的には、バグを修正する必要があるため、分割履歴には実際にはすべてのコミットが含まdirれ、予想どおりに影響します。修正には、非表示の依存関係に対する親コミットのより深い分析が含まれることを期待します。

参考までに、動作を担当する元のコードの一部を次に示します。

copy_or_skip()
  ...
  for parent in $newparents; do
      ptree=$(toptree_for_commit $parent) || exit $?
      [ -z "$ptree" ] && continue
      if [ "$ptree" = "$tree" ]; then
          # an identical parent could be used in place of this rev.
          identical="$parent"
      else
          nonidentical="$parent"
      fi
  ...
  if [ -n "$identical" ]; then
      echo $identical
  else
      copy_commit $rev $tree "$p" || exit $?
  fi

バグは2020-05-09(土)で修正されましたか?@klimkin
halfmoonhalf

バグが修正されました。参照:contrib / subtree:「サブツリー分割」のスキップされたマージのバグを修正。github.com/git/git/commit/...
halfmoonhalf

0

エリックウッドラフの答えは私を助けませんでした、しかし以下は助けました:

私は通常、「-squash」オプションを指定して「git subtree pull」します。これにより、調整が困難になったようです。そのため、今回は押しつぶさずにサブツリーのプルを実行し、いくつかの競合を解決してからプッシュする必要がありました。

押しつぶされた引きは衝突を明らかにしなかったと付け加えなければなりません、すべてが大丈夫だったと私に言いました。


0

別の[より簡単な]解決策は、可能であれば、別のコミットを行うことによってリモートのヘッドを進めることです。この高度なヘッドをローカルサブツリーにプルした後、そこから再度プッシュすることができます。


1
これが、リモートリポジトリがローカルサブツリーと同期しないという同じ問題を解決した方法です。
Aidas Bendoraitis、2018年

0

ローカルの変更をリモートのサブツリーリポジトリに強制的にプッシュできます。

git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force

0

Chris Jordanのソリューションに基づくWindowsユーザー向けの簡単なPowerShell

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"

0

これが@enthehの発言に基づいて私が書いたものです。

for /f "delims=" %%b in ('git subtree split --prefix [name-of-your-directory-on-the-file-system-you-setup] -b [name-of-your-subtree-branch]') do @set token=%%b
git push [alias-for-your-subtree] %token%:[name-of-your-subtree-branch] --force

pause

0

この問題もありました。上位の回答に基づいて、これが私がしたことです:

与えられた:

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To git@github.com:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

これを入力してください:

git push <origin> 72a6157733c4e0bf22f72b443e4ad3be0bc555ce:<branch> --force

「git subtree push」によって出力されるトークンは「git push」で使用されることに注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.