Dalam git, apakah ada cara sederhana untuk memperkenalkan cabang yang tidak terkait ke repositori?

329

Sambil membantu seorang teman dengan masalah git hari ini, saya harus memperkenalkan cabang yang harus benar-benar terpisah dari mastercabang. Isi cabang ini benar-benar memiliki asal yang berbeda dari apa yang telah dikembangkan di mastercabang, tetapi mereka akan digabung ke mastercabang di lain waktu.

Saya ingat dari membaca Git John Wiegley dari bawah ke atas bagaimana cabang pada dasarnya adalah label untuk komit yang mengikuti konvensi tertentu dan bagaimana komit diikat ke pohon file dan, opsional untuk komitmen induk. Kami pergi untuk membuat commit tanpa induk ke repositori yang ada menggunakan pipa git:

Jadi kami menyingkirkan semua file dalam indeks ...

$ git rm -rf .

... mengekstrak direktori dan file dari tarball, menambahkannya ke indeks ...

$ git add .

... dan membuat objek pohon ...

$ git write-tree

( git-write-treememberi tahu kami sha1sum dari objek pohon yang dibuat.)

Kemudian, Kami melakukan pohon, tanpa menentukan komitmen orang tua ...

$ echo "Imported project foo" | git commit-tree $TREE

( git-commit-treememberi tahu kami sha1sum dari objek commit yang dibuat.)

... dan membuat cabang baru yang menunjuk ke komit kami yang baru dibuat.

$ git update-ref refs/heads/other-branch $COMMIT

Akhirnya, kami kembali ke mastercabang untuk melanjutkan pekerjaan di sana.

$ git checkout -f master

Ini tampaknya telah bekerja sesuai rencana. Tapi ini jelas bukan jenis prosedur yang saya sarankan kepada seseorang yang baru mulai menggunakan git, untuk membuatnya lebih sederhana. Apakah ada cara yang lebih mudah untuk membuat cabang baru yang sepenuhnya tidak terkait dengan semua yang telah terjadi di repositori sejauh ini?

hillu
sumber

Jawaban:

510

Ada fitur baru (sejak V1.7.2) yang membuat tugas ini sedikit lebih tinggi daripada apa yang ada di jawaban lain.

git checkoutsekarang mendukung --orphanopsi. Dari halaman manual :

git checkout [-q] [-f] [-m] --orphan <new_branch> [<start_point>]

Buat cabang yatim baru , bernama <new_branch>, mulai dari <start_point> dan beralih ke itu. Komit pertama yang dibuat pada cabang baru ini tidak akan memiliki orang tua dan itu akan menjadi akar sejarah baru yang benar-benar terputus dari semua cabang dan komitmen lainnya.

Ini tidak melakukan persis apa yang penanya ingin, karena Mempopulai indeks dan pohon bekerja dari <start_point>(karena ini adalah, setelah semua, perintah checkout). Satu-satunya tindakan lain yang diperlukan adalah menghapus item yang tidak diinginkan dari pohon kerja dan indeks. Sayangnya, git reset --hardtidak berfungsi, tetapi git rm -rf .dapat digunakan sebagai gantinya (saya percaya ini setara dengan yang rm .git/index; git clean -fdxdiberikan dalam jawaban lain).


Singkatnya:

git checkout --orphan newbranch
git rm -rf .
<do work>
git add your files
git commit -m 'Initial commit'

Saya meninggalkan <start_point>tidak ditentukan karena default ke HEAD, dan kami tidak terlalu peduli. Urutan ini pada dasarnya melakukan hal yang sama dengan urutan perintah dalam jawaban Artem , hanya tanpa menggunakan perintah pipa yang menakutkan.

tcovo
sumber
Apakah Anda tahu jika dimungkinkan untuk membuat cabang yatim yang tetap terlihat ketika Anda checkout cabang apa pun dari "pohon" lain?
JJD
1
@JJD: Saya pikir yang Anda inginkan adalah git merge. ( git checkout master && git merge --no-commit "orphan-branch" ) Beberapa trik serupa akan bekerja menggunakan git-reset atau bermain dengan indeks. Tetapi itu tergantung pada alur kerja yang Anda inginkan.
phord
@ Matthew Ini adalah changelog untuk git 1.7.2 .
JJD
@phord Saya kurang lebih memikirkan anak yatim untuk mengandung hal-hal seperti unit test atau dokumentasi yang terpisah dari kode sumber proyek.
JJD
2
@JJD: Naskah yang saya berikan harus memberikan apa yang Anda inginkan, kecuali saya salah paham. Ketika Anda mengatakan "tetap terlihat", apakah maksud Anda file akan tetap berada di direktori kerja meskipun Anda telah memeriksa cabang yang berbeda? Menggunakan --no-commitpada git mergeakan mencapai ini. Anda mungkin perlu menindaklanjuti dengan git reset origin/masterbegitu komit Anda berikutnya pergi ke tempat yang Anda inginkan, tetapi kemudian file dari cabang yatim Anda akan muncul sebagai "file tidak terlacak" kecuali Anda juga memasukkannya dalam file .gitignore Anda.
phord
32

Dari Buku Komunitas Git :

git symbolic-ref HEAD refs/heads/newbranch 
rm .git/index 
git clean -fdx 
<do work> 
git add your files 
git commit -m 'Initial commit'
Artem Tikhomirov
sumber
1
Jawaban selanjutnya lebih baik untuk git versi modern.
kikito
14
@kikito: Re: "Jawaban selanjutnya lebih baik" ... Pemesanan di sini pada SO tidak stabil. Bisakah Anda menambahkan tautan yang mengarah ke jawaban yang menurut Anda lebih baik.
David J.
2
Saya merujuk ke stackoverflow.com/a/4288660/312586 . Anda benar, saya seharusnya tidak mengatakan "selanjutnya". Skornya kurang dari jawaban ini ketika saya berkomentar.
kikito
1
rm .git/indexjelek :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 事件
Yang ini berfungsi lebih baik untuk kasus "Saya ingin berkomitmen ke cabang, membuatnya jika belum ada sebelumnya"
max630
22

Meskipun solusi dengan git symbolic-refdan menghapus indeks berfungsi, mungkin secara konsep lebih bersih untuk membuat repositori baru

$ cd /path/to/unrelated
$ git init
[edit and add files]
$ git add .
$ git commit -m "Initial commit of unrelated"
[master (root-commit) 2a665f6] Initial commit of unrelated
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 foo

lalu ambil darinya

$ cd /path/to/repo
$ git fetch /path/to/unrelated master:unrelated-branch
warning: no common commits
remote: Counting objects: 3, done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From /path/to/unrelated
 * [new branch]      master     -> unrelated-branch

Sekarang Anda dapat menghapus / path / ke / tidak terkait

Jakub Narębski
sumber
Menurut pendapat saya, konsep bersih akan melibatkan opsi untuk git branchatau git checkout. Saya senang bahwa git memungkinkan hal semacam ini terjadi, tetapi mengapa tidak semudah itu?
hillu 06
3
Karena itu, dan harus menjadi hal yang langka. Jika Anda memiliki cabang hal-hal yang tidak terkait, itu biasanya milik repositori yang tidak terkait, alih-alih memasukkannya ke dalam yang sudah ada (walaupun ada pengecualian).
Jakub Narębski 06
1
+1. Untuk sesama pemula git, Anda dapat mendaftar cabang melalui git branchdan beralih di antara mereka git checkout BRANCH_NAME.
aliasvel
Terima kasih untuk ini, saya tidak akan memikirkannya sendiri. Ini sangat membantu karena mempertahankan sejarah (jika ada) untuk repo lainnya.
BM5k
13

Github memiliki fitur yang disebut Halaman Proyek di mana Anda dapat membuat cabang bernama tertentu di proyek Anda untuk menyediakan file yang akan dilayani oleh Github. Instruksi mereka adalah sebagai berikut:

$ cd /path/to/fancypants
$ git symbolic-ref HEAD refs/heads/gh-pages
$ rm .git/index
$ git clean -fdx

Dari sana Anda memiliki repositori kosong yang kemudian dapat Anda tambahkan konten baru.

Greg Hewgill
sumber
10

Jawaban yang dipilih saat ini benar, saya hanya akan menambahkannya secara kebetulan ...

Ini sebenarnya persis bagaimana github.com memungkinkan pengguna membuat Halaman Github untuk repo mereka, melalui cabang yatim piatu yang disebut gh-pages. Langkah-langkah cantik diberikan dan dijelaskan di sini:

https://help.github.com/articles/creating-project-pages- secara manual

Pada dasarnya perintah git untuk mengatur ini adalah sebagai berikut:

  1. git checkout --orphan gh-pages (buat cabang tanpa orangtua yang disebut gh-pages di repo Anda)
  2. git rm -rf . (menghapus semua file dari cabang pohon kerja)
  3. rm '.gitignore' (bahkan gitignore)
  4. Sekarang tambahkan konten situs web (tambahkan index.html, dll) dan komit dan dorong.
  5. Keuntungan.

Catatan Anda juga dapat menunjuk folder / docs pada repo Anda untuk menjadi sumber "Situs Proyek" yang digunakan Github untuk membangun situs web.

Semoga ini membantu!

protokol tidak diketahui
sumber
3

Kadang-kadang saya hanya ingin membuat cabang kosong di proyek secara instan kemudian mulai melakukan pekerjaan, saya hanya akan mengeluarkan perintah berikut:

git checkout --orphan unrelated.branch.name
git rm --cached -r .
echo "init unrelated branch" > README.md
git add README.md
git commit -m "init unrelated branch"
Bernyanyi
sumber
1

Jika konten Anda yang sudah ada sudah berkomitmen, Anda sekarang (Git 2.18 Q2 2018) dapat mengekstraknya ke cabang yatim sendiri, karena penerapan " git rebase -i --root" telah diperbarui untuk menggunakan mesin sequencer lebih banyak.

Sequencer itu adalah yang sekarang memungkinkan untuk mentransplantasikan seluruh topologi grafik commit di tempat lain .

Lihat komit 8fa6eea , komit 9c85a1c , komit ebddf39 , komit 21d0764 , komit d87d48b , komit ba97aea (03 Mei 2018) oleh Johannes Schindelin ( dscho) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit c5aa4bc , 30 Mei 2018)

sequencer: memungkinkan memperkenalkan commit root baru

Dalam konteks --rebase-mergesmode baru , yang dirancang khusus untuk memungkinkan untuk mengubah topologi cabang yang ada secara bebas, pengguna mungkin ingin mengekstrak komit ke cabang yang benar-benar baru yang dimulai dengan komit root yang baru dibuat .

Ini sekarang dimungkinkan dengan memasukkan perintah reset [new root]sebelum pickkomit yang ingin menjadi komit root. Contoh:

reset [new root]
pick 012345 a commit that is about to become a root commit
pick 234567 this commit will have the previous one as parent

Ini tidak bertentangan dengan penggunaan resetperintah lainnya karena [new root]ini bukan (bagian dari) nama ref yang valid: braket pembuka maupun ruang yang ilegal dalam nama ref.

VONC
sumber
0

Ditemukan skrip ini di http://wingolog.org/archives/2008/10/14/merging-in-unrelated-git-branches dan berfungsi dengan sangat baik!

#!/bin/bash

set -e

if test -z "$2" -o -n "$3"; then
    echo "usage: $0 REPO BRANCHNAME" >&2
    exit 1
fi

repo=$1
branch=$2

git fetch "$repo" "$branch"

head=$(git rev-parse HEAD)
fetched=$(git rev-parse FETCH_HEAD)
headref=$(git rev-parse --symbolic-full-name HEAD)

git checkout $fetched .

tree=$(git write-tree)

newhead=$(echo "merged in branch '$branch' from $repo" | git commit-tree $tree -p $head -p $fetched)
git update-ref $headref $newhead $head
git reset --hard $headref
Benoît
sumber
1
Saya percaya bahwa efek yang sama dapat dicapai dengan menggunakan git fetch $REMOTE $REMOTE_BRANCH:$LOCAL_BRANCH. Apakah saya melewatkan sesuatu?
hillu