Trong git, có một cách đơn giản để giới thiệu một nhánh không liên quan đến một kho lưu trữ?


328

Trong khi giúp một người bạn gặp vấn đề về git ngày hôm nay, tôi đã phải giới thiệu một chi nhánh cần tách biệt hoàn toàn với masterchi nhánh. Nội dung của chi nhánh này thực sự có nguồn gốc khác với những gì đã được phát triển trên masterchi nhánh, nhưng sau đó chúng sẽ được sáp nhập vào masterchi nhánh.

Tôi nhớ từ khi đọc Git của John Wiegley từ dưới lên về cách các nhánh về cơ bản là một nhãn cho một cam kết tuân theo một quy ước nhất định và cách một cam kết được gắn với một cây tệp và, tùy ý cho các cam kết của cha mẹ. Chúng tôi đã tạo ra một cam kết không cha mẹ với kho lưu trữ hiện có bằng cách sử dụng hệ thống ống nước của git:

Vì vậy, chúng tôi đã loại bỏ tất cả các tệp trong chỉ mục ...

$ git rm -rf .

... trích xuất các thư mục và tệp từ tarball, thêm chúng vào chỉ mục ...

$ git add .

... và tạo một đối tượng cây ...

$ git write-tree

( git-write-treenói với chúng tôi sha1sum của đối tượng cây được tạo.)

Sau đó, chúng tôi đã cam kết cây, mà không chỉ định cam kết cha mẹ ...

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

( git-commit-treenói với chúng tôi sha1sum của đối tượng cam kết đã tạo.)

... và tạo một nhánh mới trỏ đến cam kết mới được tạo của chúng tôi.

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

Cuối cùng, chúng tôi trở lại masterchi nhánh để tiếp tục làm việc ở đó.

$ git checkout -f master

Điều này dường như đã làm việc theo kế hoạch. Nhưng đây rõ ràng không phải là loại thủ tục tôi muốn giới thiệu cho người mới bắt đầu sử dụng git, nói một cách nhẹ nhàng. Có cách nào dễ dàng hơn để tạo một nhánh mới hoàn toàn không liên quan đến mọi thứ đã xảy ra trong kho lưu trữ cho đến nay?

Câu trả lời:


510

Có một tính năng mới (kể từ v1.7.2) làm cho nhiệm vụ này cao hơn một chút so với những gì trong bất kỳ câu trả lời nào khác.

git checkoutBây giờ hỗ trợ --orphantùy chọn. Từ trang người đàn ông :

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

Tạo một nhánh mồ côi mới , được đặt tên <new_branch>, bắt đầu từ <start_point> và chuyển sang nó. Cam kết đầu tiên được thực hiện trên chi nhánh mới này sẽ không có cha mẹ và nó sẽ là gốc rễ của một lịch sử mới hoàn toàn bị ngắt kết nối với tất cả các chi nhánh và cam kết khác.

Điều này không thực hiện chính xác những gì người hỏi muốn, bởi vì nó điền vào chỉ mục và cây làm việc từ đó <start_point>(vì cuối cùng, đây là lệnh thanh toán). Hành động duy nhất khác cần thiết là loại bỏ bất kỳ mục không mong muốn nào khỏi cây và chỉ mục làm việc. Thật không may, git reset --hardkhông hoạt động, nhưng git rm -rf .có thể được sử dụng thay thế (tôi tin rằng điều này tương đương với rm .git/index; git clean -fdxđưa ra trong các câu trả lời khác).


Tóm tắt:

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

Tôi đã <start_point>không xác định được vì nó mặc định là CHÍNH và dù sao chúng tôi cũng không quan tâm. Trình tự này về cơ bản giống như trình tự lệnh trong câu trả lời của Artem , mà không cần dùng đến các lệnh ống nước đáng sợ.


Bạn có biết nếu có thể tạo một nhánh mồ côi vẫn hiển thị khi bạn kiểm tra bất kỳ nhánh nào từ "cây" khác không?
JJD

1
@JJD: Tôi nghĩ những gì bạn muốn là git merge. ( git checkout master && git merge --no-commit "orphan-branch" ) Một số thủ thuật tương tự sẽ hoạt động bằng cách sử dụng git-reset hoặc chơi với chỉ mục. Nhưng nó phụ thuộc vào quy trình làm việc mong muốn của bạn.
phord

@Matthew Đây là thay đổi cho git 1.7.2 .
JJD

@phord Tôi ít nhiều nghĩ về đứa trẻ mồ côi có chứa những thứ như bài kiểm tra đơn vị hoặc tài liệu được tách ra khỏi mã nguồn của dự án.
JJD

2
@JJD: Kịch bản tôi đưa ra sẽ cung cấp cho bạn những gì bạn muốn, trừ khi tôi hiểu lầm bạn. Khi bạn nói "vẫn hiển thị", bạn có nghĩa là các tệp sẽ vẫn còn trong thư mục làm việc mặc dù bạn đã kiểm tra một nhánh khác? Sử dụng --no-committrên git mergesẽ đạt được điều này. Bạn có thể cần theo dõi git reset origin/masterđể cam kết tiếp theo của bạn đi đến nơi bạn muốn, nhưng sau đó các tệp từ chi nhánh mồ côi của bạn sẽ hiển thị dưới dạng "tệp không bị theo dõi" trừ khi bạn cũng đưa chúng vào tệp .gitignore của mình.
phord

32

Từ sách cộng đồng 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'

1
Câu trả lời tiếp theo là tốt hơn cho các phiên bản hiện đại của git.
kikito

14
@kikito: Re: "Câu trả lời tiếp theo tốt hơn" ... Việc đặt hàng ở đây trên SO không ổn định. Bạn có thể thêm một liên kết chỉ đến những gì bạn nghĩ là một câu trả lời tốt hơn.
David J.

2
Tôi đã tham khảo stackoverflow.com/a/4288660/312586 . Bạn nói đúng, tôi không nên nói "tiếp theo". Nó có ít điểm hơn câu trả lời này khi tôi nhận xét.
kikito

1
rm .git/indexlà xấu xí :-)
Ciro Santilli 冠状 病 六四 事件

Cái này hoạt động tốt hơn trong trường hợp "Tôi muốn cam kết với chi nhánh, tạo nó nếu nó không tồn tại trước đó"
max630

22

Mặc dù giải pháp với git symbolic-refvà loại bỏ chỉ mục hoạt động, nhưng về mặt khái niệm có thể sạch hơn để tạo kho lưu trữ mới

$ 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

sau đó lấy từ nó

$ 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

Bây giờ bạn có thể xóa / đường dẫn / đến / không liên quan


Theo tôi, một khái niệm sạch sẽ liên quan đến một tùy chọn git branchhoặc git checkout. Tôi rất vui vì git làm cho loại công cụ này có thể, nhưng tại sao nó không dễ dàng hơn?
hillu

3
Bởi vì nó là, và nên là một điều hiếm. Nếu bạn có nhánh những thứ không liên quan, nó thường thuộc về kho lưu trữ không liên quan, thay vì nhồi nó vào kho hiện có (mặc dù có trường hợp ngoại lệ).
Jakub Narębski

1
+1. Đối với những người mới chơi git, rõ ràng bạn có thể liệt kê các nhánh thông qua git branchvà chuyển đổi giữa chúng thông qua git checkout BRANCH_NAME.
akavel

Cảm ơn vì điều này, tôi sẽ không tự mình nghĩ về nó. Điều này rất hữu ích vì nó duy trì lịch sử (nếu có) cho các repo khác.
BM5k

13

Github có một tính năng gọi là Trang dự án nơi bạn có thể tạo một nhánh có tên cụ thể trong dự án của mình để cung cấp các tệp sẽ được Github phục vụ. Hướng dẫn của họ như sau:

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

Từ đó bạn có một kho lưu trữ trống mà sau đó bạn có thể thêm nội dung mới của mình vào đó.


10

Câu trả lời hiện được chọn là chính xác, tôi chỉ cần thêm câu đó một cách trùng hợp ...

Đây thực sự chính xác là cách github.com cho phép người dùng tạo Trang Github cho repos của họ, thông qua một nhánh mồ côi được gọi gh-pages. Các bước đẹp được đưa ra và giải thích ở đây:

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

Về cơ bản các lệnh git để thiết lập điều này như sau:

  1. git checkout --orphan gh-pages (tạo một nhánh không có cha mẹ gọi là gh-page trên repo của bạn)
  2. git rm -rf . (loại bỏ bất kỳ tập tin từ cây làm việc chi nhánh)
  3. rm '.gitignore' (ngay cả các gitignore)
  4. Bây giờ thêm nội dung trang web (thêm index.html, v.v.) và cam kết và đẩy.
  5. Lợi nhuận.

Lưu ý, bạn cũng có thể chỉ định thư mục / docs trên repo của mình để trở thành nguồn của "Trang web dự án" mà Github sử dụng để xây dựng trang web.

Hi vọng điêu nay co ich!


3

Đôi khi tôi chỉ muốn tạo nhánh trống trong dự án ngay lập tức sau đó bắt đầu thực hiện công việc, tôi sẽ chỉ thực hiện lệnh sau:

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"

1

Nếu nội dung hiện tại của bạn đã được cam kết, thì ngay bây giờ (Git 2.18 Q2 2018) có thể trích xuất nội dung đó thành chi nhánh mồ côi mới của riêng mình, do việc triển khai " git rebase -i --root" đã được cập nhật để sử dụng máy móc trình tự nhiều hơn.

Trình sắp xếp thứ tự đó là cái hiện đang cho phép ghép toàn bộ cấu trúc liên kết của đồ thị cam kết ở nơi khác .

Xem cam kết 8fa6eea , cam kết 9c85a1c , cam kết ebddf39 , cam kết 21d0764 , cam kết d87d48b , cam kết ba97aea (03 tháng 5 năm 2018) của Johannes Schindelin ( dscho) .
(Được hợp nhất bởi Junio ​​C Hamano - gitster- trong cam kết c5aa4bc , ngày 30 tháng 5 năm 2018)

sequencer: cho phép giới thiệu các cam kết gốc mới

Trong ngữ cảnh của --rebase-mergeschế độ mới , được thiết kế đặc biệt để cho phép thay đổi cấu trúc liên kết nhánh hiện tại một cách tự do, người dùng có thể muốn trích xuất các cam kết thành một nhánh hoàn toàn mới bắt đầu bằng một cam kết gốc mới được tạo .

Điều này bây giờ có thể bằng cách chèn lệnh reset [new root]trước khi picking cam kết muốn trở thành một cam kết gốc. Thí dụ:

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

Điều này không mâu thuẫn với các cách sử dụng khác của resetlệnh vì [new root]không phải (một phần) tên ref hợp lệ: cả dấu ngoặc mở cũng như khoảng trắng đều bất hợp pháp trong tên ref.


0

Tìm thấy tập lệnh này tại http://wingolog.org/archives/2008/10/14/merging-in-unrelated-git-branches và nó hoạt động rất tốt!

#!/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

1
Tôi tin rằng hiệu quả tương tự có thể đạt được bằng cách sử dụng git fetch $REMOTE $REMOTE_BRANCH:$LOCAL_BRANCH. Tui bỏ lỡ điều gì vậy?
hillu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.