Gitでの毎日の仕事

私はgitの勉強と使用をほとんどどこでも行っていません。 しかし、この間に私は多くのことを学ぶことができ、私の経験をコミュニティと共有したいと思います。



主なアイデアを伝え、このVCSがプロジェクトの開発にどのように役立つかを示します。 読んだ後に質問に答えられることを願っています:





もちろん、基本から始めて、すべてを順番に話そうとします。 したがって、この記事は、Gitを始めたばかりの人やGitを使いたい人にとって非常に役立ちます。 より経験豊富な読者は、自分自身で何か新しいものを見つけたり、エラーを指摘したり、アドバイスを共有したりするかもしれません。







計画の代わりに



非常に頻繁に、何かを始めるために、私はたくさんの資料を勉強しますが、これらは異なる人、異なる会社、異なるアプローチです。 これはすべて、何かが私に適しているかどうかを分析し理解するのに多くの時間を必要としますか? 後に、普遍的な解決策がないことを理解することになると、バージョン管理システムと開発のためにまったく異なる要件が生じます。



したがって、主な手順を強調します。





環境





動作するには、次のものが必要です。

  1. Git
  2. コンソール
  3. 好きな軸の下にすべてを置く方法を知っているモニターの反対側の男


現在の環境はDebian + KDE + Git + Bash + GitK + KDiff3です。

コンピューターにWindowsが見つかった場合、Windows + msysgit(git-bash)+ TortoiseGitなどがインストールされている可能性が高いでしょう。



コンソールを開く場合、 git



を記述してこれを取得します。

gitヘルプ
 usage: git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path] [-p|--paginate|--no-pager] [--no-replace-objects] [--bare] [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>] [-c name=value] [--help] <command> [<args>] The most commonly used git commands are: add Add file contents to the index bisect Find by binary search the change that introduced a bug branch List, create, or delete branches checkout Checkout a branch or paths to the working tree clone Clone a repository into a new directory commit Record changes to the repository diff Show changes between commits, commit and working tree, etc fetch Download objects and refs from another repository grep Print lines matching a pattern init Create an empty git repository or reinitialize an existing one log Show commit logs merge Join two or more development histories together mv Move or rename a file, a directory, or a symlink pull Fetch from and merge with another repository or a local branch push Update remote refs along with associated objects rebase Forward-port local commits to the updated upstream head reset Reset current HEAD to the specified state rm Remove files from the working tree and from the index show Show various types of objects status Show the working tree status tag Create, list, delete or verify a tag object signed with GPG See 'git help <command>' for more information on a specific command.
      
      







これで準備完了です。



実験することを恐れないでください





確かに、ほとんどのチームはすでにどこかで発見されており、いくつかの記事は読まれているので、始めたいと思っていますが、間違ったチームに参加したり、何かを壊すことを恐れています。 または、何も研究されていないかもしれません。 次に、これを覚えておいてください:

あなたは何でもでき、どんなコマンドでも実行でき、実験を設定し、削除、変更できます。 主なことはgit push



をしないことです。

このコマンドのみが、変更を別のリポジトリに転送します。 この方法でのみ、何かを壊すことができます。


厳密に言えば、失敗したgit pushも修正できます。



したがって、リポジトリを安全に複製して学習を開始できます。



リポジトリの構築





まず、gitリポジトリとは何かを理解する必要がありますか? 答えは非常に簡単です。ファイルのコレクションです。 `.git`フォルダー。 これは単なるファイルのコレクションであり、それ以上のものではないことを理解することが重要です。 約20回、私はgithub / gitlabでの承認に関する同僚の問題を見ました。 これがgitシステムの一部であると考えて、彼らはgit構成の問題を探して、いくつかのgitコマンドを呼び出しました。



そして、これらが単なるファイルである場合、何らかの方法でそれらにアクセスし、そこから読み取り、そこに書き込むことができる必要がありますか? はい! 私はそれを「トランスポート」と呼びます。 これは間違っているかもしれませんが、覚えておくととても便利でした。 より正しいオプション:「データ転送プロトコル」。 最も一般的なオプションは次のとおりです。

  1. ファイル-リポジトリファイルに直接アクセスできます。
  2. SSH-sshを介してサーバー上のファイルにアクセスできます。
  3. HTTP(S)-送受信としてhttpを使用します。


さらに多くのオプションがあります。 どのトランスポートが使用されるかは関係ありません。ファイルへの読み取りアクセスまたは読み取り/書き込みアクセスがあることが重要です。

したがって、githubを使用してリポジトリを複製できず、ログにヒントがない場合、トランスポートの問題が発生している可能性があります。



特に、このようなクローンを作成する場合:

 git clone git@github.com:user/repo.git
      
      





URLは「に変わる」

 git clone ssh://git@github.com:user/repo.git
      
      





つまり SSHが使用されているため、問題を探す必要があります。 原則として、これは正しく設定されていないか、見つからないsshキーです。 「SSH Auth Key git」の方向にグーグルで検索する必要があります。完全に大人の場合は、何が起こるかを確認してください。

 ssh -vvv git@github.com
      
      







ヘルプでサポートされているプロトコル(GIT URLSセクション):

 git clone --help
      
      







リポジトリはクローン化できますが、最初に、私たち自身で遊んでみましょう:

  1. 独自のリモートリポジトリを発明します
  2. 開発者(dev1とdev2)に代わって、そこから2つのクローンを作成しましょう






リポジトリ自体に加えて、 作業するファイルが保存されるワークスペースもあります 。 リポジトリ自体(.gitフォルダー)が存在するのは、このフォルダーです。 サーバーに作業ファイルは必要ないため、裸のリポジトリのみがそこに保存されます。



1つ作成しましょう(これがメインのテストリポジトリになります)。

 $ mkdir git-habr # ,    $ cd git-habr $ git init --bare origin Initialized empty Git repository in /home/sirex/proj/git-habr/origin/
      
      







次に、開発者に代わってクローンを作成します。 サーバーでの作業中に存在しない警告が1つだけあります。リポジトリがローカルで同じパーティションにあることを認識するgitは、リンクを作成し、完全なコピーを作成しません。 また、調査には完全なコピーが必要です。 これを行うには、 --no-hardlinks



使用するか、プロトコルを明示的に指定します。

 $ git clone --no-hardlinks origin dev1 Cloning into 'dev1'... warning: You appear to have cloned an empty repository. done. $ git clone --no-hardlinks origin dev2 Cloning into 'dev2'... warning: You appear to have cloned an empty repository. done.
      
      







結論:3つのリポジトリがあります。 そこには何もありませんが、彼らは準備ができています。



GITスタート







スキャンダル! 陰謀! 調査!




リストをさらに続けることができますが、これはすでに自然な質問をするのに十分です:

どのように機能しますか?

このすべてをどのように理解し、記憶することができますか?




これを行うには、ボンネットの下を見てください。 一般的な用語ですべてを考慮してください。



ギット。 ほぼボンネットの下


Gitは、コミットするすべてのファイルの内容を保存します(各ファイルの内容をキャストし、オブジェクトに保存します)。 ファイルが変更されていない場合、古いオブジェクトが使用されます。 したがって、 変更されたファイルのみが新しいオブジェクトの形でコミットに分類されます。これにより、多くのディスク容量が節約され、任意のコミットにすばやく切り替えることができます。



これにより、これらの面白いことがここで機能する理由を理解できます。

 $ git init /tmp/test Initialized empty Git repository in /tmp/test/.git/ $ cd /tmp/test $ cp ~/debian.iso . # iso  168  $ du -sh .git #   .git 92K .git $ git add debian.iso $ git commit -m "Added iso" [master (root-commit) 0fcc821] added iso 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 debian.iso $ du -sh .git #  163M .git # .      (   ) $ cp debian.iso debian2.iso $ cp debian.iso debian3.iso $ git add debian2.iso debian3.iso $ git commit -m "Copied iso" [master f700ab5] copied iso 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 debian2.iso create mode 100644 debian3.iso $ du -sh .git #  163M .git #   .     ,     .
      
      







はい、明白な必要なしに「重い」ファイル、バイナリなどを保存するべきではありません。 それらは永久にそこに残り、リポジトリのすべてのクローンに存在します。



各コミットには、複数の祖先コミットと複数の子コミットを含めることができます。





このツリー、またはグラフの任意のポイントに移動(状態を復元)できます。 これを行うには、git checkoutを使用します。

 git checkout <commit>
      
      





2つ以上のコミットを1つにマージすることは、マージ(2つ以上の変更セットを結合する)です。

各分岐は、いくつかのバリエーションの外観です。



ちなみに、ファイル/フォルダ、プロジェクトの一部などにタグを作成することは不可能であることに注意してください。 条件は完全にのみ復元されます。 したがって、Project1、Project2などを追加せずに、プロジェクトを別のリポジトリに保管することをお勧めします。 ただ根に。




さて、枝へ。 上に書いた:

Gitにはブランチがありません* (小さな注意事項があります)


グラフを形成する多くのコミットがあります。 親コミットから任意の子コミットへのパスを選択し、このコミットのプロジェクトの状態を取得します。 「記憶」をコミットするには、それへの名前付きポインターを作成できます。

このような名前付きポインターはブランチです。 タグも同様です。 `HEAD`は同じ原理で動作します-現在の位置を示します。 新しいコミットは、現在のブランチの継続です(HEADが見る場所と同じです)。



ポインターは、タグでない場合、任意のコミットに自由に移動できます。 この目的のために、タグはコミットを一度だけ記憶し、どこにも移動しないようにします。 ただし、削除することはできます。

おそらく、gitで作業するときに初めて理論から知る必要があるのはこれだけでしょう。 これで、残りの部分はより理解しやすくなるはずです。



用語


インデックス -記録された変更の領域、つまり リポジトリに保存するために準備したすべてのもの。

commit-リポジトリに送信された変更。

HEADは、現在のコミットへのポインタです。

master-デフォルトのブランチ名。これは特定のコミットへのポインタでもあります

origin-デフォルトでのリモートリポジトリの名前(別のものを指定できます)

チェックアウト -リポジトリから状態を取得します。



簡単な編集


常にあなたの指先にあるべき2つのことがあります:

  1. git status
  2. gitk




あなたが何か間違ったことをした、混乱した、何が起こっているのかわからない-これらの2つのチームがあなたを助けます。



git status



リポジトリ(作業コピー)のステータスと現在の場所を表示します。

gitk



は、グラフを表示するグラフィカルユーティリティです。 キーとして、ブランチの名前または--all



を渡してすべてを表示します。



先ほど作成したリポジトリに戻りましょう。 さらに、1人の開発者がdev1 $で作業し、2人目の開発者がdev2 $で作業することをマークします。



README.mdを追加します。

 dev1$ vim README.md dev1$ git add README.md dev1$ git commit -m "Init Project" [master (root-commit) e30cde5] Init Project 1 file changed, 4 insertions(+) create mode 100644 README.md dev1$ git status # On branch master nothing to commit (working directory clean)
      
      







みんなと共有します。 しかし、空のリポジトリを複製したため、デフォルトではgitはコミットを追加する場所を知りません。

彼はこれを教えてくれます:

 dev1$ git push origin No refs in common and none specified; doing nothing. Perhaps you should specify a branch such as 'master'. fatal: The remote end hung up unexpectedly error: failed to push some refs to '/home/sirex/proj/git-habr/origin' dev1$ git push origin master Counting objects: 3, done. Writing objects: 100% (3/3), 239 bytes, done. Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] master -> master
      
      







2番目の開発者は、プルを実行することでこれらの変更を取得できます。

 dev2$ git pull remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /home/sirex/proj/git-habr/origin * [new branch] master -> origin/master
      
      







さらにいくつかの変更を追加します。

 dev1(master)$ vim README.md dev1(master)$ git commit -m "Change 1" -a dev1(master)$ vim README.md dev1(master)$ git commit -m "Change 2" -a dev1(master)$ vim README.md dev1(master)$ git commit -m "Change 3" -a
      
      







何をしたのか見てみましょう(gitkを実行):

非表示のテキスト


最初のコミットをハイライトしました。 下から上に順番に移動すると、リポジトリがどのように変化したかを確認できます。

 @@ -2,3 +2,4 @@ My New Project -------------- Let's start +Some changes
      
      





 @@ -3,3 +3,5 @@ My New Project Let's start Some changes +Some change 2 +
      
      





 @@ -2,6 +2,5 @@ My New Project -------------- Let's start -Some changes -Some change 2 +Some change 3
      
      









これまで、コミットを最後に追加しました( マスターは )。 ただし、README.mdの別のバージョンを追加できます。 そして、どこからでもできます。 たとえば、最後のコミットが気に入らず、別のオプションを試しています。 前のポイントでブランチポインターを作成します。 これを行うには、git logまたはgitkを通じてコミットIDを学習します。 次に、ブランチを作成して切り替えます:

 dev1(master)$ git branch <branch_name> <commit_id> #    git branch <branch_name> HEAD~1 #     dev1(master)$ git checkout <branch_name>
      
      







GUIが好きな人には、さらに簡単なオプションがあります。右マウスボタンで正しいコミットを選択-> "新しいブランチを作成"。

表示されるブランチをクリックすると、このブランチアイテムがチェックアウトされます。 ブランチに「v2」という名前を付けました。

テストを変更しましょう:

 dev1(v2)$ vim README.md dev1(v2)$ git commit -m "Ugly changes" -a [v2 75607a1] Ugly changes 1 file changed, 1 insertion(+), 1 deletion(-)
      
      





次のようになります。





これで、どこからでもブランチが作成される方法と、その履歴がどのように変化するかがわかりました。



高速巻き戻し


確かに、あなたはすでにfast-forwardrebasemerge mergeという言葉に出会っています。 これらの概念に対処する時が来ました。 私はリベースを使用しますが、誰かがマージするだけです。 リベースとマージのテーマはたくさんあります。 著者はしばしば、自分の方法がより良く、より便利であると納得させようとします。 私たちは逆に行きます:それが何であり、どのように機能するかを理解します。 その後、どのオプションをどのケースで使用するかがすぐに明らかになります。



これまでのところ、1つのリポジトリの制限内で、ブランチを作成します。ファイルを作成し、リポジトリに配置し、新しいポイントからファイルの2つのバリアントを作成し、すべてをマスターに結合しようとします。

 dev1(v2)$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 3 commits. # ,      master   origin
      
      





次の内容でcollider.init.shファイルを作成します。

 #!/bin/sh USER=collider case $1 in *) echo Uknown Action: $1 ;; esac
      
      







新しいブランチで開発を追加、コミット、開始します。

 dev1(master)$ git add collider.init.sh dev1(master)$ git commit -m "Added collider init script" [master 0c3aa28] Added collider init script 1 file changed, 11 insertions(+) create mode 100755 collider.init.sh dev1(master)$ git checkout -b collider/start #   Switched to a new branch 'collider/start' dev1(collider/start)$ git checkout -b collider/terminate #   Switched to a new branch 'collider/terminate'
      
      





git checkout -b <branch_name>は、現在位置へのポインター(branch)<branch_name>を作成し(現在位置は特別なHEADポインターを使用して追跡されます)、それに切り替えます。

または単に:現在の場所から新しいブランチを作成し、すぐにそれを続行します。



ブランチ名に「/」文字を使用することは禁じられていないことに注意してください。ただし、注意が必要です。 「/」までの名前のフォルダがファイルシステムに作成されます。 フォルダーと同じ名前のブランチが存在する場合、ファイルシステムレベルで競合が発生します。 devブランチがすでにある場合、 dev / testを作成できません。

また、 dev / testあればdev / whateverを作成できますが、 devだけではできません。



そのため、2つのブランチcollider / startcollider / terminateを作成しました。 混乱した? gitk-救助にすべて



ご覧のとおり、ある時点で3つのポインター(ブランチ)があり、コミットの変更は次のとおりです。

 @@ -0,0 +1,11 @@ +#!/bin/sh + + +USER=collider + + +case $1 in + *) + echo Uknown Action: $1 + ;; +esac
      
      





次に、各ブランチで、コライダーを起動および破棄するコードを記述します。 アクションのシーケンスはおよそ次のとおりです。

 dev1(collider/start)$ vim collider.init.sh dev1(collider/start)$ git commit -m "Added Collider Start Function" -a [collider/start d229fa9] Added Collider Start Function 1 file changed, 9 insertions(+) dev1(collider/start)$ git checkout collider/terminate Switched to branch 'collider/terminate' dev1(collider/terminate)$ vim collider.init.sh dev1(collider/terminate)$ git commit -m "Added Collider Terminate Function" -a [collider/terminate 4ea02f5] Added Collider Terminate Function 1 file changed, 9 insertions(+)
      
      





行われた変更
コライダー/スタート

 @@ -3,8 +3,17 @@ USER=collider +do_start(){ + echo -n "Starting collider..." + sleep 1s + echo "ok" + echo "The answer is 42. Please, come back again after 1 billion years." +} case $1 in + start) + do_start + ;; *) echo Uknown Action: $1 ;;
      
      







コライダー/終了



 @@ -3,8 +3,17 @@ USER=collider +do_terminate() { + echo -n "Safely terminating collider..." + sleep 1s + echo "oops :(" + +} case $1 in + terminate) + do_terminate + ;; *) echo Uknown Action: $1 ;;
      
      









いつものように、 gitkで確認してみましょう。





開発が完了し、すべての変更をマスターに与える必要があります(何もできない古いコライダーがあります)。 上記のように、2つのコミットをマージすることはmergeです。 しかし、 マスターブランチがコライダー/スタートとどのように異なるのか、そしてそれらの結合(合計)を取得する方法について考えましょうか? たとえば、これらのブランチの一般的なコミットを取得しmasterのみに関連するコミットを追加してから、 collider / startのみに関連するコミットを追加できます。 私たちはどうですか? 一般的なコミットがあり、 マスターコミットのみ-いいえ、 コライダー/スタートのみ-があります。 つまり これらのブランチをマージするには、 master + collider / startからコミットします。 しかし、 コライダー/スタートマスター + コライダー/コミットのコミットです! 同じこと! つまり 何もしない! ブランチのマージ-これはコライダー/スタートです!

繰り返しますが、文字のみで、理解しやすくなることを願っています。

マスター= C1 + C2 + C3

コライダー/スタート=マスター+ C4 = C1 + C2 + C3 + C4

master + collider / start = General_commit(master、collider / start)+ Only_y(master)+ Only_y(collider / start)=(C1 + C2 + C3)+(NULL)+(C4)= C1 + C2 + C3 + C4



あるブランチが別のブランチに「横たわっている」場合、すでに別のブランチに入るようになっており、マージの結果は2番目のブランチになります。 古いコミットから新しいコミットに履歴を巻き戻すだけです。 この巻き戻し(何もする必要のない結合)は、早送りと呼ばれます。

なぜ早送りが良いのですか?

  1. マージするとき、何もする必要はありません
  2. さらに、自動関連付けが保証されます
  3. 競合は不可能であり、決して発生しません
  4. ストーリーは(関連性がないかのように)線形のままであり、大規模なプロジェクトでは理解しやすいことが多い
  5. 新しいコミットは表示されません




早送りが可能なものをすばやく見つける方法 これを行うには、組み合わせて1つの質問に答える必要がある2つのブランチのgitkを見てください。上から(下から上へ)だけ移動する場合、ブランチAからBへの直接パスがあります。 はいの場合、早送りが行われます。

理論的には明らかです。実際に試してみて、変更をマスターに渡します。

 dev1(collider/terminate)$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 4 commits. dev1(master)$ git merge collider/start Updating 0c3aa28..d229fa9 Fast-forward #    collider.init.sh | 9 +++++++++ 1 file changed, 9 insertions(+)
      
      





結果(ポインタが先に進みました):





統一


次に、変更をコライダー/ターミネートから取得します 。 しかし、ここまで読んだ人(結局読んで、はい?!)は、直接の道はないことに気付くでしょう。 早送りを要求するためにgitを試してみましょう:

 dev1(master)$ git merge --ff-only collider/terminate fatal: Not possible to fast-forward, aborting.
      
      





予想されることです。 マージするだけです:

 dev1(master)$ git merge collider/terminate Auto-merging collider.init.sh CONFLICT (content): Merge conflict in collider.init.sh Automatic merge failed; fix conflicts and then commit the result.
      
      





葛藤があることも嬉しいです。 通常、この時点で、一部の人は迷子になり、Googleで何をすべきかを尋ねます。

開始するには:

同じ行で変更が行われた2つ以上のコミットを結合しようとすると、競合が発生します。 そして今、gitは何をすべきかを知りません。最初のオプションを選択するか、2番目のオプションを選択するか、古いオプションを残すか、すべてを削除します。


いつものように、最も必要な2つのチームは私たちを助けるために急いでいます:

 dev1(master)$ git status # On branch master # Your branch is ahead of 'origin/master' by 5 commits. # # Unmerged paths: # (use "git add/rm <file>..." as appropriate to mark resolution) # # both modified: collider.init.sh # no changes added to commit (use "git add" and/or "git commit -a") dev1(master)$ gitk --all
      
      







私たちはmasterにいます。HEADは同じものを指し、コミットもそこに追加されます。

ファイルは次のようになります。

 #!/bin/sh USER=collider <<<<<<< HEAD do_start(){ echo -n "Starting collider..." sleep 1s echo "ok" echo "The answer is 42. Please, come back again after 1 billion years." } case $1 in start) do_start ======= do_terminate() { echo -n "Safely terminating collider..." sleep 1s echo "oops :(" } case $1 in terminate) do_terminate >>>>>>> collider/terminate ;; *) echo Uknown Action: $1 ;; esac
      
      





両方のオプションが必要ですが、手動で結合したくありませんか? ここでは、すべてのマージツールが役立ちます。

競合を解決する最も簡単な方法は、 git mergetoolコマンドを呼び出すことです。 何らかの理由で、誰もがそのようなチームについて知っているわけではありません。

彼女は次のようなことをします。

  1. diffまたはmergeプログラムの選択を見つけて提供する
  2. ファイルfilename.orig(マージを試みる前のファイル)を作成します
  3. ファイルfilename.base(作成されたファイル)を作成します
  4. ファイルfilename.remoteを作成します(別のブランチで変更されたため)
  5. ファイルfilename.localを作成します(変更したとおり)
  6. 競合を解決した後、すべてがファイル名で保存されます


私達は試みます:

 dev1(master)$ git mergetool merge tool candidates: opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse ecmerge p4merge araxis bc3 emerge vimdiff Merging: collider.init.sh Normal merge conflict for 'collider.init.sh': {local}: modified file {remote}: modified file Hit return to start merge resolution tool (kdiff3):
      
      





変更が同じ競合の場所にあったという事実により、多くのことが起こりました。 したがって、私はどこでも最初のオプションを取り、2番目のオプションを最初のオプションの直後にコピーしましたが、guiを使用すると非常に簡単です。 結果は次のとおりです。上部にファイルオプションがあり、下部にユニオンがあります(品質については申し訳ありません)。



結果を保存し、ウィンドウを閉じてコミットし、結果を確認します。





他の2つの結合である新しいコミットを作成しました。 このための直接的な道がなかったため、早送りは行われませんでした。ストーリーは少し混乱し始めました。 マージが本当に必要な場合もありますが、過度に複雑なストーリーも役に立ちません。

実際のプロジェクトの例を次に示します。



もちろん、これはダメです! 「しかし、開発は並行して行われ、早送りはありません」とあなたは言いますか? 抜け道があります!



再編


ストーリーを美しくダイレクトに保つ​​ために何ができますか? ブランチを取得して、別のブランチで再構築できます! つまり ブランチを新しい開始点に向け、すべてのコミットを1つずつ再生します。このプロセスはリベースと呼ばれます。コミットは、再構築するブランチの継続となります。そうすれば、物語はシンプルで線形になります。そして、早送りが可能になります。

言い換えれば、あるブランチから別のブランチへの変更の履歴を繰り返します。あたかも別のブランチを取得して同じ変更を再作成したかのように。



まず、最新の変更を元に戻します。最も簡単な方法は、マスターポインターを以前の状態に戻すことです。 merge-commitを作成し、正確にmasterを移動しました。したがって、それを戻す必要があります。同じコミットが望ましい(そして場合によっては重要)ことがあります。

その結果、マージコミットはポインターなしで残り、どのブランチにも属しません。



gitk



またはコンソールを使用して、ポインターを移動します。collider / startブランチはすでにコミットを指しているので、そのIDを探す必要はありませんが、ブランチ名を使用できます(これは同じことです):

 dev1(master)$ git reset --hard collider/start HEAD is now at d229fa9 Added Collider Start Function
      
      







merge-commitはどうなりましたか?
(), . Git , , .. , . , git garbage collector .

, . git reflog . , ( HEAD ). , id , checkout ( ).

( , ):

 d229fa9 HEAD@{0}: reset: moving to collider/start 80b77c3 HEAD@{1}: commit (merge): Merged collider/terminate d229fa9 HEAD@{2}: merge collider/start: Fast-forward 0c3aa28 HEAD@{3}: checkout: moving from collider/terminate to master 4ea02f5 HEAD@{4}: commit: Added Collider Terminate Function 0c3aa28 HEAD@{5}: checkout: moving from collider/start to collider/terminate d229fa9 HEAD@{6}: commit: Added Collider Start Function 0c3aa28 HEAD@{7}: checkout: moving from collider/launch to collider/start 0c3aa28 HEAD@{8}: checkout: moving from collider/terminate to collider/launch 0c3aa28 HEAD@{9}: checkout: moving from collider/stop to collider/terminate 0c3aa28 HEAD@{10}: checkout: moving from collider/start to collider/stop 0c3aa28 HEAD@{11}: checkout: moving from master to collider/start 0c3aa28 HEAD@{12}: commit: Added collider init script 41f0540 HEAD@{13}: checkout: moving from v2 to master 75607a1 HEAD@{14}: commit: Ugly changes 55280dc HEAD@{15}: checkout: moving from master to v2 41f0540 HEAD@{16}: commit: Change 3 55280dc HEAD@{17}: commit: Change 2 598a03a HEAD@{18}: commit: Change 1 d80e5f1 HEAD@{19}: commit (initial): Init Project
      
      









:のは、何が起こったのか見てみましょう





その後、コミットに取り、別のブランチを再構築するためには、彼らの共通の起源を見つける必要が調整可能な枝をして、同じ順序で、にそれらを置くメイン(ベース)支店。非常に明確な画像(機能はマスターリビルド中です):



重要な注意:「perestroika」の後、これらは新しいコミットになります。そして、古いものは消えず、どこにも移動しませんでした。



: merge-commit. collider/terminate collider/start .

collider/terminate collider/start , master merge-commit . , , master ( git checkout master && git reset --hard collider/terminate ). , . Git — , .





私たちは理論を理解し、実際に試してみました。コライダー/終了に切り替えマスターがポイントするコミットに再構築します(または、コライダー/開始、より便利な人)。コマンドは文字通り「現在のブランチを取得し、指定されたコミットまたはブランチで再構築します」:

 dev1(master)$ git checkout collider/terminate Switched to branch 'collider/terminate' dev1(collider/terminate)$ git rebase -i master # -i    , ..  git rebase todo list      
      
      





エディターが開き、およそ次のものが表示されます。

 pick 4ea02f5 Added Collider Terminate Function # Rebase d229fa9..4ea02f5 onto d229fa9 # # 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. #
      
      





つまり、ペレストロイカの過程で、コミットに関するコメントを変更したり、コミット自体を編集したり、マージしたり、完全にスキップしたりできます。 つまりブランチの履歴を認識せずに書き換えることができます。この段階では、これは必要ありません。エディターを閉じて続行します。前回同様、競合を避けることはできません。

 error: could not apply 4ea02f5... Added Collider Terminate Function When you have resolved this problem run "git rebase --continue". If you would prefer to skip this patch, instead run "git rebase --skip". To check out the original branch and stop rebasing run "git rebase --abort". Could not apply 4ea02f5... Added Collider Terminate Function dev1((no branch))$ git status # Not currently on any branch. # Unmerged paths: # (use "git reset HEAD <file>..." to unstage) # (use "git add/rm <file>..." as appropriate to mark resolution) # # both modified: collider.init.sh # no changes added to commit (use "git add" and/or "git commit -a")
      
      





git mergetool使用して競合を解決し、 "rebuild" -git rebase --continueを続行します。Gitはインタラクティブに変更およびコメントする機能を提供します。

結果:



これで、マスターを更新して不要なものをすべて削除することはもはや難しくありません:

 dev1(collider/terminate)$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 5 commits. dev1(master)$ git merge collider/terminate Updating d229fa9..6661c2e Fast-forward collider.init.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) dev1(master)$ git branch -d collider/start Deleted branch collider/start (was d229fa9). dev1(master)$ git branch -d collider/terminate Deleted branch collider/terminate (was 6661c2e).
      
      







この段階で、編集を削除、編集、マージし、出力では、変化の美しい線形履歴を取得しました。





休憩


かなり多くの情報がすでに受信されているので、停止してすべてについて考える必要があります。例を使用して、次のgit機能に精通しました。





さらなる例はより複雑になります。ファイルにランダムな行を追加する簡単なスクリプトを使用します。

この段階では、どのようなコンテンツでも構いませんが、1つだけでなく、さまざまなコミットを用意することをお勧めします。明確にするために。

スクリプトはファイルにランダムな行を追加し、gitコミットを作成します。これは数回繰り返されます:

 dev1(master)$ for i in `seq 1 2`; do STR=`pwgen -C 20 -B 1`; echo $STR >> trash.txt; git commit -m "Added $STR" trash.txt; done [master e64499d] Added rooreoyoivoobiangeix 1 file changed, 1 insertion(+) [master a3ae806] Added eisahtaexookaifadoow 1 file changed, 1 insertion(+)
      
      







変更の送信と受信





リモートリポジトリを操作する方法を学ぶ時が来ました。一般的に言えば、仕事には次のことができる必要があります。





使用される主なコマンドのリストは次のとおりです。

  1. git remote



    -リモートリポジトリの管理
  2. git fetch



    -取得
  3. git pull



    - git fetch



    + と同じgit merge



  4. git push



    -送信




git remote


上記のように、originはデフォルトのリポジトリ名です。名前が必要です いくつかのリポジトリが存在する可能性があり、何らかの方法で区別する必要があります。たとえば、フラッシュドライブにリポジトリのコピーがあり、フラッシュリポジトリを追加しましたしたがって、originflashの 2つのリポジトリを同時に使用できます



リポジトリ名はブランチ名のプレフィックスとして使用されるため、ブランチを別のブランチと区別することができます(例:masterおよびorigin / master)



ちょっとしたトリック
master origin origin/master . , '/'.

つまり " origin\/master ", master . Git , , . , .





ヘルプgit remote



ドキュメントは非常によく説明されています。予想どおり、コマンドがあります:add、rm、rename、show。

show



基本的なリポジトリ設定が表示されます:

 dev1(master)$ git remote show origin * remote origin Fetch URL: /home/sirex/proj/git-habr/origin Push URL: /home/sirex/proj/git-habr/origin HEAD branch: master Remote branch: master tracked Local branch configured for 'git pull': master merges with remote master Local ref configured for 'git push': master pushes to master (fast-forwardable)
      
      







既存のリポジトリを追加するには、次を使用しますadd





 git remote add backup_repo ssh://user@myserver:backups/myrepo.git #   git push backup_repo master
      
      







git fetch


チームはそれを代弁します。変更を取得します。

ローカルで変更がないことは注目に値します。Gitは作業コピーに触れず、ブランチにも触れません。

新しいコミットがダウンロードされ、リモートブランチとタグのみが更新されます。これは、リポジトリを更新する前に、すべての変更を確認できるため便利です。



以下はプッシュコマンドの説明ですが、フェッチがどのように機能するかを明確に示すために、変更をoriginに渡す必要があります。

 dev1(master)$ git push origin master Counting objects: 29, done. Delta compression using up to 4 threads. Compressing objects: 100% (21/21), done. Writing objects: 100% (27/27), 2.44 KiB, done. Total 27 (delta 6), reused 0 (delta 0) Unpacking objects: 100% (27/27), done. To /home/sirex/proj/git-habr/origin d80e5f1..a3ae806 master -> master
      
      







次に、dev2を代表して、何が何であるかを確認し、すべての変更を取得します。

 dev2(master)$ git log commit d80e5f1746856a7228cc27072fa71f1c087d649a Author: jsirex Date: Thu Apr 4 04:21:07 2013 +0300 Init Project #   ,  : dev2(master)$ git fetch origin remote: Counting objects: 29, done. remote: Compressing objects: 100% (21/21), done. remote: Total 27 (delta 6), reused 0 (delta 0) Unpacking objects: 100% (27/27), done. From /home/sirex/proj/git-habr/origin d80e5f1..a3ae806 master -> origin/master
      
      







それは次のようになります。



私たちがしていることに注意してくださいマスター

できること:

git checkout origin/master



-リモートマスターに切り替えて「タッチ」します。このブランチを編集することはできませんが、独自のローカルブランチを作成して操作できます。

git merge origin/master



-新しい変更と独自の変更を組み合わせます。なぜなら ローカルに変更がなかった場合、マージは早送りに変わります。

 dev2(master)$ git merge origin/master Updating d80e5f1..a3ae806 Fast-forward README.md | 2 ++ collider.init.sh | 31 +++++++++++++++++++++++++++++++ trash.txt | 2 ++ 3 files changed, 35 insertions(+) create mode 100755 collider.init.sh create mode 100644 trash.txt
      
      







新しいブランチがオリジンに表示される場合、fetch自体もそれらをダウンロードします。また、すべてではなく特定のブランチのみ更新するようにフェッチを要求することもできます。

重要なポイント:誰かがオリジンからブランチを削除するとき、あなたはまだローカルでそれについてのローカルレコードを持っています。たとえば、origin / deleted / branchは引き続き表示されますが、もう存在しません。これらのエントリを削除するには、を使用しますgit fetch origin --prune







git pull


git pull



git fetch + git mergeと同じです。もちろん、変更は対応するブランチとマージされます:master with origin / masterfeature with origin / featureブランチは、誰かが考えるように名前で結合されていませんが、上流の追跡ブランチのためです。作業のためにブランチをチェックアウトすると、gitは次のようなことをします。

  1. 既にそのようなブランチがローカルに存在するかどうかを調べ、存在する場合はそれを取得します
  2. ブランチがない場合は、リモートにあるかどうかを調べます/ <branch_name>
  3. 存在する場合origin / <branch_name>同じ場所にローカルに<branch_name>を作成し、これらのブランチを「リンク」します(ブランチ<branch_name>は現在、リモートのorigin / <branch_name>を追跡しています


これは.git / configファイルで確認できます

 [branch "master"] remote = origin merge = refs/heads/master
      
      







95%の場合、この動作を変更する必要はありません。



ローカルで変更があった場合、git pull



それらはリモートブランチと自動的にマージされ、早送りではなくマージコミットが行われます。何かを開発している間、コードは古くなっているため、最初にアップグレードし、新しいコードの上にすべての変更を適用してから結果を出すのがいいでしょう。または、今のところローカルで続行します。そして、物語が混ざらないように。これはこれを行うのに役立ちますrebase





gitがデフォルトrebase



ではなくを使用するmerge



ように、あなたはそれを尋ねることができます:

 git config branch.<branch_name>.rebase true
      
      







git push


git push origin



-変更を転送します。この場合、すべての追跡ブランチが送信されます。特定のブランチを伝えるために、明示的に名前を指定する必要がありますgit push origin branch_name







この段階で、疑問が生じる場合があります。





コマンドの拡張ユースケースは、すべての質問に答えることができます。

git push origin <___>:<___origin>





例:

 git push origin d80e5f1:old_master #       old_master.     d80e5f1 git push origin my_local_feature:new_feature/with_nice_name #     new_feature/with_nice_name  my_local_feature git push origin :dead_feature #   ""  dead_feature. ..  dead_feature   .   
      
      







重要:ブランチを更新するとき、最初にすべての最新の変更を取得してから、すべてを元に戻そうとすることが想定されます。Gitはストーリーが線形のままであると想定し、現在の変更を継続します(早送り)。それ以外の場合は、以下を受け取ります:rejected! non fast-forward push!





時々、あなたが何をしているかを正確に知っているとき、それは必要です。これを回避できます:

 git push origin master --force #    ,  
      
      







私たちはプロジェクトに含まれています





この時点で、プッシュ、プルがどのように機能するかは多かれ少なかれ明らかです。さらに曖昧なのは、マージとリベースの違いです。これがなぜ必要で、どのように適用するのかは完全に不明です。

誰かに尋ねられたら:

-なぜバージョン管理システムが必要なのですか?

ほとんどの場合、あなたは聞くことができます:

-このシステムは、プロジェクトのすべての変更を保存するのに役立ち、何も失われず、いつでも「ロールバック」できます。



ここで、「どのくらいの頻度でロールバックしなければならないのか」という質問を自問してください。プロジェクトの最後の状態よりも多くを保存する必要がありますか?正直な答えは「非常にまれ」です。この質問を提起して、プロジェクトにおけるバージョン管理システムのはるかに重要な役割を強調します。

バージョン管理システムにより、複数の開発者がプロ​​ジェクトで共同作業できます。




プロジェクトで他の開発者と一緒に作業するのがいかに便利で、バージョン管理システムがどれだけこれを支援しているかが最も重要なことです。



%VCS_NAME%を使用していますか?便利ですか?開発プロセスを制限し、要件に簡単に適応しますか?速い?したがって、この%VCS_NAME%はプロジェクトに最適です。おそらく、何も変更する必要はありません。



典型的なプロジェクトシナリオ




それでは、コードの観点からプロジェクトに取り組む典型的なシナリオについて話しましょう。そしてこれ:





このプロセスを実現するには、どこに何を保存するかを明確に決定する必要があります。なぜならすべてのタスクに軽量のブランチ(つまり、リソースや場所などを費やさない)の場合、個別のブランチを作成できます。これは良い習慣です。このアプローチにより、一連の変更を簡単に操作したり、それらをさまざまなブランチに含めたり、失敗したオプションを完全に排除したりすることができます。マスターは、通常、最後にリリースされたバージョンを保持し、唯一のリリースからリリースに更新されます。次のように、バージョンを示すタグを作成する必要はありません。マスターが更新される場合があります。メインの開発用にもう1つのブランチが必要になりますこれはすべてがマージされ、masterの継続となるブランチです。これはテストされ、準備ができたらリリース中にすべての変更がmasterにマージされます。このスレッドを呼び出しましょう





DEV

修正プログラムをリリースする必要がある場合、マスター状態または特定のタグに戻り修正プログラム/バージョンブランチを作成して、重要な変更の修正作業を開始できます。これは、設計および進行中の開発には影響しません。feature / <feature_name>

ブランチを使用して機能を開発すると便利です。このスレッドを最新の変更で開始し、devから自分に定期的に変更を「プル」することをお勧めします。ストーリーをシンプルかつ直線的に保つには、devgit rebase dev



)でブランチを再構築することをお勧めします。

マイナーなバグの修正はに直接行くことができますDEVただし、小さなバグや変更があっても、ローカルに一時的なブランチを作成することをお勧めします。それらをグローバルリポジトリに送信する必要はありません。これらは一時的なブランチにすぎません。このアプローチは多くの機会を提供します:





バグ修正


devブランチ作成して、典型的なバグ修正シナリオを考えてみましょう

 dev1(master)$ git status # On branch master nothing to commit (working directory clean) dev1(master)$ git checkout -b dev Switched to a new branch 'dev' dev1(dev)$ git push origin dev Total 0 (delta 0), reused 0 (delta 0) To /home/sirex/proj/git-habr/origin * [new branch] dev -> dev
      
      





そして、2番目の開発者は、自分自身に新しいブランチを「取り入れ」ます。

 dev2(master)$ git pull From /home/sirex/proj/git-habr/origin * [new branch] dev -> origin/dev Already up-to-date.
      
      







2人の開発者がそれぞれ独自のバグに取り組み、いくつかのコミットを行うようにします(このようない方法で多くのランダムコミットを生成するという事実と混同しないでください)。

 dev1(dev)$ for i in `seq 1 10`; do STR=`pwgen -C 20 -B 1`; echo $STR >> trash.txt; git commit -m "Added $STR" trash.txt; done [dev 0f019d0] Added ahvaisaigiegheweezee 1 file changed, 1 insertion(+) [dev c715f87] Added eizohshochohseesauge 1 file changed, 1 insertion(+) [dev 9b9672c] Added aitaquuerahshiqueeph 1 file changed, 1 insertion(+) [dev 43dad98] Added zaesooneighufooshiph 1 file changed, 1 insertion(+) [dev 9da2de3] Added aebaevohneejaefochoo 1 file changed, 1 insertion(+) [dev e93f93e] Added rohmohpheinugogaigoo 1 file changed, 1 insertion(+) [dev 54ba433] Added giehaokeequokeichaip 1 file changed, 1 insertion(+) [dev 05f72db] Added hacohphaiquoomohxahb 1 file changed, 1 insertion(+) [dev 8c03e0d] Added eejucihaewuosoonguek 1 file changed, 1 insertion(+) [dev cf21377] Added aecahjaokeiphieriequ 1 file changed, 1 insertion(+)
      
      





 dev2(master)$ for i in `seq 1 6`; do STR=`pwgen -C 20 -B 1`; echo $STR >> trash.txt; git commit -m "Added $STR" trash.txt; done [master 1781a2f] Added mafitahshohfaijahney 1 file changed, 1 insertion(+) [master 7df3851] Added ucutepoquiquoophowah 1 file changed, 1 insertion(+) [master 75e7b2b] Added aomahcaashooneefoavo 1 file changed, 1 insertion(+) [master d4dea7e] Added iexaephiecaivezohwoo 1 file changed, 1 insertion(+) [master 1459fdb] Added quiegheemoighaethaex 1 file changed, 1 insertion(+) [master 1a949e9] Added evipheichaicheesahme 1 file changed, 1 insertion(+)
      
      







作業が終了したら、変更をリポジトリに転送します。Dev1:

 dev1(dev)$ git push origin dev Counting objects: 32, done. Delta compression using up to 4 threads. Compressing objects: 100% (30/30), done. Writing objects: 100% (30/30), 2.41 KiB, done. Total 30 (delta 19), reused 0 (delta 0) Unpacking objects: 100% (30/30), done. To /home/sirex/proj/git-habr/origin a3ae806..cf21377 dev -> dev
      
      







2番目の開発者:

 dev2(master)$ git push origin dev error: src refspec dev does not match any. error: failed to push some refs to '/home/sirex/proj/git-habr/origin'
      
      







どうしたまず、私たちはやったがpull



devに切り替えるのを忘れていました。そして彼らはマスターで働き始めました

gitを使用するときは、できるだけ具体的に「コマンド」を実行してください。

変更をdevに渡したいと思いました。代わりgit push origin dev



に単純に使用した場合git push





gitは必要な処理を行います- マスターからorigin / masterに変更を渡します。この状況は修正できますが、より複雑です。すべてをローカルで修正する方がはるかに簡単です。





上記のように、それは大丈夫です、すべてが修正可能です。いつものように、のは、彼女が行っていたか見てみましょうgitk --all







私たちは前進を持っている「»運転したマスターを、しかしdevに「行く」必要がありました状況を修正するためのいくつかのオプションがあります。

ここでは、たとえば次のようにできます。

  1. 他に変更がなかったため、devに切り替えます
  2. masterからdevへのすべての変更を結合します(直接パス、早送り、問題なし)。
  3. 正しいdevを実行し、間違ったマスターを以前の正しい位置に設定します。以前の位置(origin / master




一見、多くのアクションがあり、すべてが何らかの形で複雑になっているようです。しかし、見てみると、何をする必要があるかという説明は、作業そのものよりもはるかに多くなっています。そして、最も重要なことは、すでに上記で行ったことです。練習しましょう:

 dev2(master)$ git checkout dev #  Branch dev set up to track remote branch dev from origin. Switched to a new branch 'dev' dev2(dev)$ git merge master #  fast-forward Updating a3ae806..1a949e9 Fast-forward trash.txt | 6 ++++++ 1 file changed, 6 insertions(+) dev2(dev)$ git checkout master #   master ... Switched to branch 'master' Your branch is ahead of 'origin/master' by 6 commits. dev2(master)$ git reset --hard origin/master # ...       HEAD is now at a3ae806 Added eisahtaexookaifadoow dev2(master)$ git checkout dev #   dev Switched to branch 'dev' Your branch is ahead of 'origin/dev' by 6 commits.
      
      







結果を見てみましょう:



今、すべてがそうあるべきです:私たちの変更はdevにあり、originに渡す準備ができています。

合格:

 dev2(dev)$ git push origin dev To /home/sirex/proj/git-habr/origin ! [rejected] dev -> dev (non-fast-forward) error: failed to push some refs to '/home/sirex/proj/git-habr/origin' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Merge the remote changes (eg 'git pull') hint: before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
      
      







他の誰か(dev1)がリポジトリを更新し、変更を渡し、私たちは単に「遅れた」ため、転送は機能しませんでした。git pull



またはを作成して状態を更新する必要がありgit fetch



ます。なぜならgit pullはすぐにブランチをマージします。gitfetchを使用することを好みます。後で見て回って決定する機会を与えてくれます。

 dev2(dev)$ git fetch origin remote: Counting objects: 32, done. remote: Compressing objects: 100% (30/30), done. remote: Total 30 (delta 19), reused 0 (delta 0) Unpacking objects: 100% (30/30), done. From /home/sirex/proj/git-habr/origin a3ae806..cf21377 dev -> origin/dev
      
      







変更を伝えるためのいくつかのオプションがあります。

  1. そこにあったものを消去して、変更を強制的に転送します。 git push origin dev --force



  2. を使用してブランチを続行しgit merge origin/dev



    、次にgit push origin dev



    (新しい変更を独自の変更とマージして転送します)
  3. ブランチを新しいブランチの一番上に再構築します。これにより、開発シーケンスが保持され、不要なブランチが不要にgit rebase origin/dev



    なりgit push origin dev



    ます。


最も魅力的なのは3番目のオプションです。さらに、インタラクティブモードで試してみます。

 dev2(dev)$ git rebase -i origin/dev #      origin/dev
      
      





エディターが開き、履歴を修正する機会を提供します。たとえば、コミットの順序を変更したり、いくつかを結合したりします。これは上に書かれています。そのままにしておきます。

 pick 1781a2f Added mafitahshohfaijahney pick 7df3851 Added ucutepoquiquoophowah pick 75e7b2b Added aomahcaashooneefoavo pick d4dea7e Added iexaephiecaivezohwoo pick 1459fdb Added quiegheemoighaethaex pick 1a949e9 Added evipheichaicheesahme # Rebase cf21377..1a949e9 onto cf21377 # # 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. #
      
      







ペレストロイカの過程で、対立が生じます。git mergetool



(または必要に応じて)解決し、リストラを続けますgit rebase --continue





なぜなら私は常に単一のファイルに行を追加していますが、競合は避けられませんが、実際のプロジェクトでは、プロジェクトのさまざまな部分の作業が分散されると、競合はまったく発生しません。または、非常にまれです。

ここから、いくつかの便利なルールを導き出すことができます





すべての競合が解決されると、ストーリーは線形かつ論理的になります。明確にするために、コメントを変更しました(インタラクティブなリベースがこの機会を与えます):



ブランチはorigin / devを継続し、変更を送信できます:関連、新しいコミットに適応:

 dev2(dev)$ git push origin dev Counting objects: 20, done. Delta compression using up to 4 threads. Compressing objects: 100% (18/18), done. Writing objects: 100% (18/18), 1.67 KiB, done. Total 18 (delta 11), reused 0 (delta 0) Unpacking objects: 100% (18/18), done. To /home/sirex/proj/git-habr/origin cf21377..8212c4b dev -> dev
      
      





さらに、物語は繰り返されます。上記のように、便宜上、またはいくつかのバグに関する作業の場合、作業を開始する前にdevまたはorigin / devから個別のブランチを作成すると便利です。



短い要約:通常のバグ修正は、次の簡単なアルゴリズムを使用して実行できます。

  1. devブランチまたはセパレートの修正
  2. オリジンからの変更の取得(git fetch origin



  3. 変更を最新バージョンにリビルドします(git rebase -i origin/dev



  4. 変更をオリジンに渡す(git push origin dev







機能ブランチ


大きな作業を行う必要がある場合がありますが、それは終了するまでメインバージョンに分類されるべきではありません。いくつかのブランチはそのようなブランチで動作できます。非常に深刻で膨大なバグは、gitを機能として処理するプロセスの観点から考えることができます-数人が作業する別のブランチです。プロセス自体は、通常のバグ修正とまったく同じです。唯一の作業はdevではなく、機能/名前ブランチで行われます。

一般に、このようなブランチは短命(1〜2週間)と長命(1か月以上)になります。もちろん、ブランチの寿命が長くなればなるほど、頻繁に更新する必要があり、メインブランチから変更を「プル」します。ブランチが大きいほど、それに伴うオーバーヘッドが大きくなります。



短命の機能ブランチから始めましょう


通常、ブランチは最後のコードから作成されます。この場合、devブランチから作成されます。

 dev1(dev)$ git fetch origin remote: Counting objects: 20, done. remote: Compressing objects: 100% (18/18), done. remote: Total 18 (delta 11), reused 0 (delta 0) Unpacking objects: 100% (18/18), done. From /home/sirex/proj/git-habr/origin cf21377..8212c4b dev -> origin/dev dev1(dev)$ git branch --no-track feature/feature1 origin/dev #   feature/feature1  origin/dev,      dev1(dev)$ git push origin feature/feature1 #     ,      Total 0 (delta 0), reused 0 (delta 0) To /home/sirex/proj/git-habr/origin * [new branch] feature/feature1 -> feature/feature1
      
      







今feature1の作業はfeature / feature1ブランチで行うことができます。しばらくすると、ブランチに多くのコミットがあり、feature1の作業が完了します。同時に、devにも多くの変更が加えられます。そして、私たちのタスクは、変更をdevに送信することです。状況は





ようになります。状況は前の状況に似ています。2つのブランチ、1つを他のブランチとマージし、リポジトリに転送する必要があります。唯一の違いは、ローカルのブランチではなく、2つのパブリック(リモート)ブランチです。これには少しコミュニケーションが必要です。作業が終了したら、開発者の1人は、devの変更を「マージ」し、たとえばブランチを削除することを他の開発者に警告する必要があります。したがって、このスレッドに何かを転送しても意味がありません。

そして、アルゴリズムは以前とほとんど同じです:

 git fetch origin #    git rebase -i origin/dev # ,     ,    feature1    dev   .  origin/dev,   dev       ,       git checkout dev #   ,      git merge feature/feature1 #   git reset --hard feature/feature1.       .  ,   fast-forward.
      
      





画像、feature1はdevの一部になり、次に必要なもの:



不要なものをプッシュしてクリーンアップします:

 dev1(dev)$ git push origin dev Counting objects: 11, done. Delta compression using up to 4 threads. Compressing objects: 100% (9/9), done. Writing objects: 100% (9/9), 878 bytes, done. Total 9 (delta 6), reused 0 (delta 0) Unpacking objects: 100% (9/9), done. To /home/sirex/proj/git-habr/origin 3272f59..e514869 dev -> dev dev1(dev)$ git push origin :feature/feature1 #   ,   To /home/sirex/proj/git-habr/origin - [deleted] feature/feature1 dev1(dev)$ git branch -d feature/feature1 #   Deleted branch feature/feature1 (was e514869).
      
      







長期にわたる機能ブランチ


サポートと更新における長寿命ブランチの複雑さ。上記のようにすべてを行うと、災害が発生する可能性があります。時間がなくなり、メインブランチが変更され、プロジェクトが変更され、機能が非常に古いバージョンに基づいています。機能をリリースするときが来ると、プロジェクトからノックアウトされてしまい、組合は非常に困難になるか、不可能になる可能性さえあります。そのため、ブランチを更新する必要があります。 devが前進したら、時々devからブランチを再構築するだけです。



すべては問題ありませんが、publicブランチを取得して再構築することはできません。リベース後のブランチはすでに新しいコミットセットであり、まったく異なる話です。彼女はすでに行ったことを継続しません。 Gitはそのような変更を受け入れません。2つの異なるパス、早送りはありません。ストーリーを書き直すには、開発者が同意する必要があります。

誰かが履歴を書き換えて新しいバージョンを強制しますが、現時点では、残りは現在のブランチに変更を転送しないでください。それは上書きされ、すべてがなくなります。最初の開発者が終了すると、他の全員がコミットを転送します。これは、すでに新しいブランチで行われている国勢調査中に行う時間です。誰があなたがパブリックブランチをリベースできないと言ったのですか?



練習しましょう:

 dev1(dev)$ git checkout -b feature/long Switched to a new branch 'feature/long' dev1(feature/long)$ git push origin dev Everything up-to-date dev1(feature/long)$ __ dev1(feature/long)$ git push origin feature/long Counting objects: 11, done. Delta compression using up to 4 threads. Compressing objects: 100% (9/9), done. Writing objects: 100% (9/9), 807 bytes, done. Total 9 (delta 6), reused 0 (delta 0) Unpacking objects: 100% (9/9), done. To /home/sirex/proj/git-habr/origin * [new branch] feature/long -> feature/long
      
      







2番目の開発者は、すべてに標準的に接続します:

 dev2(dev)$ git pull remote: Counting objects: 11, done. remote: Compressing objects: 100% (9/9), done. remote: Total 9 (delta 6), reused 0 (delta 0) Unpacking objects: 100% (9/9), done. From /home/sirex/proj/git-habr/origin * [new branch] feature/long -> origin/feature/long Already up-to-date. dev2(dev)$ git checkout feature/long Branch feature/long set up to track remote branch feature/long from origin. Switched to a new branch 'feature/long' dev2(feature/long)$    dev2(feature/long)$ git pull --rebase feature/long # fetch + rebase   dev2(feature/long)$ git push origin feature/long Counting objects: 11, done. Delta compression using up to 4 threads. Compressing objects: 100% (9/9), done. Writing objects: 100% (9/9), 795 bytes, done. Total 9 (delta 6), reused 0 (delta 0) Unpacking objects: 100% (9/9), done. To /home/sirex/proj/git-habr/origin baf4c6b..ce9e58d feature/long -> feature/long
      
      





メインのdevブランチにさらにいくつかのコミットを追加すると、ストーリーは次のようになります。feature / long





を更新する時が来ましたが、開発は個別に続行する必要があります。dev1を再構築します。その後、彼はそれについてdev2に警告し、開始します。

 dev1(feature/long)$ git fetch origin dev1(feature/long)$ git rebase -i origin/dev ...  ,  ,  ..
      
      





現時点では、dev2は引き続き動作しますが、プッシュできないことを知っています。目的のブランチはまだありません(現在のブランチは削除されます)。

最初のリベースはリベースし、ストーリーは次のようになります。



ブランチはリビルドされ、元の場所/機能/長い場所はそのままでした。私たちは目標を達成しました。次は全員と共有する必要があります。

 dev1(feature/long)$ git push origin feature/long To /home/sirex/proj/git-habr/origin ! [rejected] feature/long -> feature/long (non-fast-forward) error: failed to push some refs to '/home/sirex/proj/git-habr/origin' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Merge the remote changes (eg 'git pull') hint: before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
      
      





Gitは、何かが間違っていることを改めて思い出させます。しかし今、私たちは何をしているかを正確に知っており、それが必要であることを知っています。

 dev1(feature/long)$ git push origin feature/long --force Counting objects: 20, done. Delta compression using up to 4 threads. Compressing objects: 100% (18/18), done. Writing objects: 100% (18/18), 1.58 KiB, done. Total 18 (delta 12), reused 0 (delta 0) Unpacking objects: 100% (18/18), done. To /home/sirex/proj/git-habr/origin + ce9e58d...84c3001 feature/long -> feature/long (forced update)
      
      





これで、作業の完了について他の人に警告することにより、さらに作業を進めることができます。



これらの変更が他の人、特にdev2にどのように影響したかを見てみましょう。

 dev2(feature/long)$ git fetch origin remote: Counting objects: 20, done. remote: Compressing objects: 100% (18/18), done. remote: Total 18 (delta 12), reused 0 (delta 0) Unpacking objects: 100% (18/18), done. From /home/sirex/proj/git-habr/origin + ce9e58d...84c3001 feature/long -> origin/feature/long (forced update)
      
      









歴史は分かれており、コミットは1つを除いて再構築されています。コミットがまったくなかった場合、つまりdev2開発者はハブを読み取り、最初のハブは機能しますfeature/long



、ポインター動かしてorigin/feature/long



作業を続けるだけで十分です。ただし、追加する必要があるコミットが1つあります。ここで、rebaseがキーとともに再び役立ちます--onto





git rebase --onto <___> <c____> <_____>





このアプローチでは、再構築のために一部(コミットのシーケンス)のみを取ることができます。最後のいくつかのコミットを引き継ぐ必要がある場合に便利です。

記録についても思い出してくださいHEAD~1



。 〜N演算子をポインターに適用して、前のN番目のコミットを指すことができます。

HEAD~1



-これは前のものですが、HEAD~2



-これは前のものです。HEAD~5



-5回コミット。 IDを覚えていないのに便利です。



コミットを1つだけ再構築する方法を見てみましょう。

 git rebase -i --onto origin/feature/long feature/long~1 feature/long
      
      





さらに詳しく分析しましょう。

  1. 私たちは機能/ロングにあります。これは新しいものとは完全に異なりますが、必要なコミットが含まれています(1、最後)
  2. origin / feature / longへの再構築を命令します(ブランチでの新しい開始)
  3. ブランチ自体はこの機能/長いです
  4. しかし、すべてを再構築する必要はありません(共通の開始の検索とすべてのコミットの再構築は不要であり、既に存在します)が、前のコミットから始めます(包括的ではありません)。 つまり機能/ロング〜1


4つのコミットをドラッグする必要がある場合は、になりますfeature/long~4





 dev2(feature/long)$ git rebase -i --onto origin/feature/long feature/long~1 feature/long Successfully rebased and updated refs/heads/feature/long.
      
      







作業を継続することは残っています。この場合や他の多くの場合に役立つ



別の便利なコマンドがあります- git cherry-pick





どこからでも、gitにコミットを依頼して現在の場所に適用することができます。必要なコミットは「引きずり捨てられる」可能性があります。

 git reset --hard origin/feature/long && git cherry-pick commit_id # commit_id   , ..  reset     
      
      







修正プログラム


修正プログラムが必要ですできるだけ早く修正する必要がある非常に重大なバグがあります。同時に、ホットフィックスと一緒に最後のコードを転送することはできません。テストされておらず、完全なテストの時間はありません。可能な限り迅速にテストされたこの修正のみが必要です。これを行うには、実稼働環境に送信された最新リリースを使用します。これは、タグまたはマスターを残した場所です。タグは、何が正確に収集され、どこで取得されたかを理解する上で非常に重要な役割を果たします。

タグはチームによって作成されます
 git tag     '/'   . 
      



hotfix- :

hotfix tag master ( cherry-pick , - ) cherry-pick , . .

git:

dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev








    git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  1. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  2. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  3. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  4. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  5. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  6. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  7. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  8. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  9. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




  10. git tag '/' .



    hotfix- :





    hotfix tag master ( cherry-pick , - ) cherry-pick , . .

    git:

    dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev




git tag '/' .



hotfix- :





hotfix tag master ( cherry-pick , - ) cherry-pick , . .

git:

dev2(feature/long)$ git checkout master Switched to branch 'master' dev2(master)$ git tag release/1.0 # , dev2(master)$ git checkout -b hotfix/1.0.x Switched to a new branch 'hotfix/1.0.x' dev2(hotfix/1.0.x)$ .. dev2(hotfix/1.0.x)$ git push origin hotfix/1.0.x Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 302 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin * [new branch] hotfix/1.0.x -> hotfix/1.0.x dev2(hotfix/1.0.x)$ git tag release/1.0.1 # dev2(hotfix/1.0.x)$ git checkout dev # Switched to branch 'dev' dev2(dev)$ git cherry-pick release/1.0.1 # id, . # , git commit dev2(dev)$ git commit [dev 9982f7b] Added rahqueiraiheinathiav 1 file changed, 1 insertion(+) dev2(dev)$ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 328 bytes, done. Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/sirex/proj/git-habr/origin b21f8a5..9982f7b dev -> dev








すばやく簡単にできますか?ホットフィックス2つのコミットがある場合cherry-pick



devで 2回行う方が簡単かもしれませんただし、それらが多数ある場合は、コマンドgit rebase --into ...



再び使用して、コミットのチェーン全体を再構築できます



あらゆる種類の有用性



Gitでは、さまざまなチームのエイリアスをカスタマイズできます。これは非常に便利で、入力時間を短縮できます。ここでは例を挙げません。それらはインターネットでいっぱいです。見てください。



変更を加える前に、履歴を整理するために自分で変更を再構築できます。

たとえば、ログでは次のようになります。

 9982f7b Finally make test works b21f8a5 Fixed typo in Test 3eabaab Fixed typo in Test e514869 Added Test for Foo b4439a2 Implemented Method for Foo 250adb1 Added class Foo
      
      





自分自身を再構築しましょう。ただし、6コミット前から開始します。

 dev2(dev)$ git rebase -i dev~6 # ..       origin/dev       dev2(dev)$ git rebase -i origin/dev
      
      





インタラクティブモードでは、最初の2つのコミットを1つと最後の4つのコミットに結合(スカッシュ)できます。次に、ストーリーは次のようになります。

 0f019d0 Added Test for class Foo a3ae806 Implemented class Foo
      
      





これは、共有リポジトリに渡す方がはるかに優れています。



git config



することができるかを見てくださいgit config --global



実際のプロジェクトで作業を開始する前に、ユーザー名とメールアドレスを設定しておくと便利です。

 git config --global user.name sirex git config --global user.email jsirex@gmail.com
      
      







1つのプロジェクトで、多くの機能の並行開発を実施しました。それらはすべて別々のブランチで開発されましたが、一緒にテストする必要がありました。同時に、テスト用のビルドリリースの時点では、機能の準備が整っているかどうかは明確ではありませんでした。要件が変更されたり、重大なバグが発見されたりする可能性がありました。そして質問は、原則としてそれらをすべて組み合わせずに、リリース前にすべてを組み合わせてすべての機能の作業を続ける方法ですか?論争?Gitはこの問題の解決に役立ちます。その方法を次に示します。

  1. ビルドをリリースするために、すべての機能がマージされた別個のブランチが作成されました(git merge --no-ff



  2. ブランチはテスト用に提供されました
  3. 開発は支店で継続
  4. 新しい変更が再びマージされました(git merge --no-ff



  5. , .
  6. - , dev .
  7. , ,




結論











gccGit Extensionsをご覧になることを推奨しました

borNfreeは、別のソースツリー GUIクライアントを促しました

zloylosは、git

olanchegビジュアライザーへのリンクを共有し、初心者向けの別のチュートリアルを参照することを提案しました



PS。 ハブに最初の投稿をするとき、彼らは通常何を書きますか?厳しく判断しないでください、できる限り書きました。



All Articles