В git есть простой способ ввести несвязанную ветку в репозиторий?


329

Помогая другу с проблемой git сегодня, я должен был представить ветку, которая должна быть полностью отделена от masterветви. Содержимое этой ветки действительно имеет происхождение, отличное от того, что было разработано в masterветке, но они будут объединены в masterветку позже.

Я вспомнил, читая Git Джона Уигли снизу вверх, как ветви по сути являются меткой для коммита, который следует определенному соглашению, и как коммит привязан к дереву файлов и, необязательно, к родительским коммитам. Мы пошли, чтобы создать коммит без родительского контроля в существующем репозитории с использованием git-системы:

Таким образом, мы избавились от всех файлов в индексе ...

$ git rm -rf .

... извлек каталоги и файлы из архива, добавил их в индекс ...

$ git add .

... и создал объект дерева ...

$ git write-tree

( git-write-treeрассказал нам sha1sum созданного объекта дерева.)

Затем мы зафиксировали дерево, не указывая родительские коммиты ...

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

( git-commit-treeрассказал нам sha1sum созданного объекта коммита.)

... и создал новую ветку, которая указывает на наш недавно созданный коммит.

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

Наконец, мы вернулись в masterфилиал, чтобы продолжить там работу.

$ git checkout -f master

Кажется, это сработало, как и планировалось. Но это явно не та процедура, которую я бы порекомендовал кому-то, кто только начинает пользоваться git, мягко говоря. Есть ли более простой способ создания новой ветки, которая совершенно не связана со всем, что происходило в хранилище до сих пор?

Ответы:


511

Существует новая функция (начиная с V1.7.2), которая делает эту задачу немного более высокоуровневой, чем в любом другом ответе.

git checkoutтеперь поддерживает --orphanопцию. Со страницы руководства :

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

Создать новый сирота филиал, названный <new_branch>, начинается с <start_point> и переключиться на него. Первый коммит, сделанный в этой новой ветке, не будет иметь родителей, и он станет корнем новой истории, полностью отсоединенной от всех других ветвей и коммитов.

Это не делает именно то, что хотел запрашивающий, потому что он заполняет индекс и рабочее дерево <start_point>(поскольку это, в конце концов, команда checkout). Единственное другое необходимое действие - удалить ненужные элементы из рабочего дерева и индекса. К сожалению, git reset --hardне работает, но git rm -rf .может использоваться вместо этого (я считаю, что это эквивалентно rm .git/index; git clean -fdxприведенным в других ответах).


В итоге:

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

Я оставил <start_point>неуказанным, потому что по умолчанию это HEAD, и нам все равно все равно. Эта последовательность, по сути, делает то же самое, что и последовательность команд в ответе Артема , просто не прибегая к страшным сантехническим командам.


Знаете ли вы, возможно ли создать сиротскую ветвь, которая остается видимой, когда вы извлекаете любую ветку из другого «дерева»?
JJD

1
@JJD: я думаю, что вы хотите, это мерзавец слияния. ( git checkout master && git merge --no-commit "orphan-branch" ) Некоторые похожие трюки будут работать с использованием git-reset или игры с индексом. Но это зависит от вашего желаемого рабочего процесса.
phord

@ Matthew Вот список изменений для git 1.7.2 .
JJD

@phord Я более или менее думал, что сирота будет содержать такие вещи, как модульные тесты или документацию, отделенную от исходного кода проекта.
JJD

2
@JJD: Сценарий, который я дал, должен дать вам то, что вы хотите, если я вас не неправильно понял. Когда вы сказали «остается видимым», вы имеете в виду, что файлы останутся в рабочем каталоге, даже если вы выбрали другую ветку? Использование --no-commiton git mergeпозволит достичь этого. Возможно, вам понадобится выполнить git reset origin/masterследующий ваш коммит, куда вы хотите, но тогда файлы из вашей сиротской ветви будут отображаться как «неотслеживаемые файлы», если вы не включите их в свой файл .gitignore.
Phord

32

Из Git Community Book :

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
Следующий ответ лучше для современных версий git.
kikito

14
@kikito: Re: «Следующий ответ лучше» ... Порядок здесь на SO не стабилен. Не могли бы вы добавить ссылку, указывающую на то, что вы считаете лучшим ответом?
Дэвид Дж.

2
Я имел в виду stackoverflow.com/a/4288660/312586 . Вы правы, я не должен был говорить «дальше». У него было меньше очков, чем у этого ответа, когда я комментировал.
kikito


Этот вариант лучше
подходит

22

Хотя решение с git symbolic-refиндексом и без него работает, концептуально может быть проще создать новый репозиторий.

$ 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

тогда извлекай из него

$ 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

Теперь вы можете удалить / путь / к / не связан


На мой взгляд, чистая концепция будет включать вариант git branchили git checkout. Я рад, что git делает подобные вещи возможными, но почему бы не быть проще?
Hillu

3
Потому что это так и должно быть редкостью. Если у вас есть несвязанная ветвь вещей, она обычно принадлежит не связанному репозиторию, а не заполняет его в существующем (хотя есть исключения).
Якуб Наребски

1
+1. Для новичков Git вы, очевидно, можете затем перечислить ветви черезgit branch и переключаться между ними через git checkout BRANCH_NAME.
akavel

Спасибо за это, я бы не подумал об этом сам. Это очень полезно, потому что он поддерживает историю (если таковая имеется) для другого репо.
BM5k

13

Github имеет функцию под названием Project Pages, где вы можете создать определенную именованную ветку в вашем проекте, чтобы предоставить файлы, которые будут обслуживаться Github. Их инструкции следующие:

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

Оттуда у вас есть пустой репозиторий, в который вы можете добавить свой новый контент.


10

Текущий выбранный ответ правильный, я бы просто добавил это по совпадению ...

Это именно то, как github.com позволяет пользователям создавать страницы Github для своих репозиториев через осиротевшую ветвь gh-pages. Красивые шаги даны и объяснены здесь:

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

В основном команды git для настройки этого следующие:

  1. git checkout --orphan gh-pages (создайте ветку без родителей в репозитории gh-pages)
  2. git rm -rf . (удаляет все файлы из рабочего дерева ветки)
  3. rm '.gitignore' (даже Гитиньор)
  4. Теперь добавьте содержимое веб-сайта (добавьте index.html и т. Д.), Выполните коммит и нажмите.
  5. Прибыль.

Обратите внимание, что вы также можете указать папку / docs в вашем репо как источник «сайта проекта», который Github использует для создания сайта.

Надеюсь это поможет!


3

Иногда я просто хочу мгновенно создать пустую ветку в проекте, а затем начать делать работу, просто извиняюсь за следующую команду:

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

Если ваш существующий контент уже был зафиксирован, вы теперь (Git 2.18 Q2 2018) можете извлечь его в свою собственную новую сиротскую ветвь, так как реализация "git rebase -i --root " была обновлена, чтобы больше использовать механизм секвенсора.

Этот секвенсор теперь позволяет перенести всю топологию графа коммитов в другое место .

См. Коммит 8fa6eea , коммит 9c85a1c , коммит ebddf39 , коммит 21d0764 , коммит d87d48b , коммит ba97aea (03 мая 2018 г.) от Johannes Schindelin ( dscho) .
(Слиты Junio C Hamano - gitster- в фиксации c5aa4bc , 30 мая 2018)

секвенсор: позволяет вводить новые корневые коммиты

В контексте нового --rebase-mergesрежима, который был специально разработан, чтобы позволить свободно изменять существующую ветвь топологии, пользователь может захотеть извлечь коммиты в совершенно новую ветвь, которая начинается с вновь созданного корневого коммита .

Теперь это возможно, вставив команду reset [new root]перед pickкоммитом, который хочет стать корневым коммитом. Пример:

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

Это не противоречит другим применениям resetкоманды, поскольку [new root]не является (частью) действительного имени ссылки: и открывающая скобка, и пробел недопустимы в именах ссылок.


0

Нашел этот скрипт по адресу http://wingolog.org/archives/2008/10/14/merging-in-unrelated-git-branches и он работает очень хорошо!

#!/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
Я считаю, что тот же эффект может быть достигнут с помощью git fetch $REMOTE $REMOTE_BRANCH:$LOCAL_BRANCH. Я что-то упускаю?
Хиллу

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.