В 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.