Используйте git orphan в качестве папок и сохраняйте историю - PullRequest
0 голосов
/ 20 марта 2019

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

  1. Как мне взять существующее репо, импортировать его как ветвь сирот и сохранить историю?

  2. Можно ли работать с 2-мя сиротскими ветками, открытыми как разные папки?Допустим, у меня есть 2 сиротских филиала, и я хочу работать над ними обоими параллельно, возможно?У меня гораздо больше двух, и я хочу работать с открытым git-интерфейсом, чтобы его было эффективнее разрабатывать.Сегодня я проверяю каждый репозиторий.после того, как я объединю его под тем же репо, мне все равно придется работать на всех ветках параллельно.

orphan-branch-B
              \.gitignore
              \.README.md
              \ and more

orphan-branch-B
              \.gitignore
              \.README.md
              \ and more

Ответы [ 2 ]

1 голос
/ 20 марта 2019

TL; DR

У меня гораздо больше, чем 2 [ветви], и я хочу работать с одним открытым интерфейсом git ...

Возможно ли это,и если да, то как, зависит от пользовательского интерфейса.Если вы спросите о Git вообще, вы не получите ответ на этот вопрос.Ответом командной строки является использование git worktree (для которого крайне желательно Git 2.15 или новее).

Long

  1. Как мне взять существующее репо,импортировать его как ветку-сироту и сохранить историю?

Не совсем, правда.Эта операция - и вопрос - вероятно, не имеет смысла, потому что я сомневаюсь, что вы имеете в виду сиротская ветвь в том смысле, как это означает Git .Прочтите до конца, чтобы решить, имеет ли это смысл.

Что такое репозиторий Git?

Репозиторий Git состоит, по сути, из двух баз данных.Одна база данных просто содержит объекты Git , наиболее интересным из которых является commit , и каждый коммит представляет полный снимок всех файлов. 1 Другая база данных содержит names - имена ветвей, такие как master, и имена тегов, такие как v2.1, - и эти имена - то, как вы, и, по крайней мере, изначально Git, найдете интересных * 1039 коммитов.

Каждый коммит, который, опять же, представляет снимок всех файлов;коммиты не содержат изменений - уникально идентифицируются по его хэш-идентификатору.Идентификатор хэша представляет собой большую некрасивую строку букв и цифр, которая выглядит случайной, но на самом деле представляет собой криптографическую контрольную сумму всего содержимого коммита: снимка и метаданных, которые сообщают вам, кто сделал снимок (имя и адрес электронной почты), когда (отметка времени), почему (сообщение журнала) и т. д.Поскольку каждый коммит хранит фактический хеш-идентификатор своего непосредственного предшественника или родительский коммит, Git легко начать с last commit и работать в обратном направлении:

... <-F <-G <-H   <-- master

Следовательно, имя ветви , как master, просто содержит хэш-идентификатор last commit в ветви.Сама история - это просто цепочка коммитов, сформированная, начиная с этого коммита - H в этом примере - и работая в обратном направлении, по одному коммиту за раз, от коммита к родителю.

Здесь есть небольшая задержка, потому чтоцепи не обязательно являются линейными.Сделав последовательность коммитов, как указано выше, мы можем также иметь вторую последовательность коммитов:

          G--H  <-- master
         /
...--E--F
         \
          I--J   <-- develop

Здесь коммиты F и более ранние находятся на обеих ветвях, в то время как коммитыG-H являются только на master и I-J являются только на develop.Если затем объединить J в master, мы получим коммит, который немного особенный:

          G--H
         /    \
...--E--F      K  <-- master
         \    /
          I--J   <-- develop

Хотя коммит K имеет простой снимок, как обычно, он теперь имеет двое родителей, а не один, что делает коммит слияния .Чтобы просмотреть историю из K, мы должны вернуться к коммитам H и J и одновременно.Оттуда мы возвращаемся к G и I;оттуда мы возвращаемся к F, где история снова сходится, расходясь при слиянии.

Другими словами, Git работает в обратном направлении: история логически сходится при слиянии, а поскольку Git работает в обратном направлении,история на самом деле расходится при слиянии.История логически расходится в том месте, где вы выделили вторую ветку, но в Git она на самом деле сходится в этой точке, потому что Git работает в обратном направлении.

Что делает имя ветви как и master особенным является то, что всегда указывает на последний коммит, который мы хотим сказать, находится на ветви .Это особенно важно, потому что вы спрашиваете о сиротских ветвях .


1 Три других типа объектов: дерево (деревья содержат имена файлов), blob (каждый BLOB-объект является содержимым файла) и аннотированный тег для таких тегов, какv2.1.Git использует комбинацию commit + tree + blob для создания снимка, который представляет каждый коммит.


Как Git делает новые коммиты: индекс и рабочее дерево

Возможно ли работать с 2-мя ветвями-сиротами, открытыми как разные папки?

Если у вас Git 2.5 или новее - с 2.15 или новее будет хорошей идеей из-за некоторых ошибок вначальная реализация в Git 2.5 - вы можете использовать git worktree для одновременной работы с двумя разными ветвями в двух разных рабочих деревьях .Настало время поговорить о понятиях Git index и work-tree , после чего мы перейдем к определению сиротской ветви .

Все в моментальном снимке коммита Git заморожено навсегда.Никакая часть какого-либо коммита - ни его лог-сообщение, ни имя пользователя, ни родительский хэш-идентификатор, ни какая-либо часть любого сохраненного файла, хранящегося как часть этого коммита - не может бытьизменилось. Ничто в любой существующей фиксации, идентифицируемой некоторым существующим хеш-идентификатором, не может быть изменено. Все его файлы заморожены во времени.(Они также сжаты, иногда очень сжаты. Вы можете думать о них как о сублимированных, если хотите.) Это отлично подходит для архивирования: вы можете вернуться ко времени любого предыдущего коммита, в любое время, когда захотите.Но это бесполезно для выполнения любой новой работы.

Чтобы позволить вам выполнить работу, Git дает вам возможность проверить коммит.Проверка коммита делает три вещи:

  1. Первое и наиболее очевидное - это то, что он как бы «ре гидратирует» лиофилизированный коммит, извлекая все его файлы в некоторый видрабочая область, где они имеют свою нормальную, незамерзшую, не мерзкую форму.Эта рабочая область, которая обычно находится непосредственно рядом с самим хранилищем, является вашим рабочим деревом (или рабочим деревом, или иногда рабочим каталогом, или некоторым вариантом этого вида написания.)

  2. Второе, также очевидное, если подумать, это то, что если вы используете git checkout master или git checkout develop или что-то еще, это то, что он запоминает , какое имя ветки вы использовали для получения последнего коммита отэта ветвь .Или, если вы использовали git checkout <hash-id> для возврата во времени, он запоминает хэш-идентификатор.В любом случае - по имени ветки или по хэш-идентификатору - он запоминает, какой коммит у вас тоже есть.

  3. Третья, в основном невидимая вещь, которая git checkout делает здесь, чтобы заполнить Git index .

Называя эту вещь index является своего рода бесполезным именем - что делает index передают в конце концов? - так что у него есть еще два имени: его иногда называют промежуточной областью , или иногда кеш , в зависимости от того, кто или какая частьGit делает это призвание.Все три имени для одной и той же вещи, хотя.Что такое индекс и что он делает, становится немного сложным во время слияний, но главное, что он делает и делает, это то, что он содержит все файлы из коммита в их Git-ified форме, готов для замораживания, но- в отличие от реального коммита - на самом деле не заморожен .

Это означает, что индекс содержит все файлы, которые будут включены в следующий коммит.Другими словами, это своего рода следующий предлагаемый коммит.Вы начинаете с:

git checkout master

и для каждого файла, который был в коммите, идентифицированном именем master, теперь у вас есть не две, а три копииэтого файла:

  • HEAD:file - это файл, сохраненный в коммите.Он не может быть изменен: он Git-ified, заморожен и доступен только для чтения.Используйте git show HEAD:file, чтобы увидеть его.

  • :file is файл, сохраненный в index .Это можно изменить!Это Git-ified, но вы можете заменить на новую копию в любое время.Используйте git show :file, чтобы увидеть его.

  • file - это файл, хранящийся в вашем рабочем дереве.Это обычный файл, и вы можете делать с ним все, что захотите.Используйте обычные (не Git) команды для просмотра или изменения или делайте что хотите.

Если вы изменили какой-нибудь файл, например file, ивы хотите, чтобы Git сохранял новую версию в следующем коммите, теперь вы должны обновить предложенный следующий коммит:

git add file

Это копий файл рабочего дерева в индекс, перезаписывая :file новой Git-ified копией вашего файла file из вашего рабочего дерева.

Следовательно, индекс всегда содержит предложенныйследующий коммит .Вы обновляете это предложение, используя git add.

Обратите внимание, что если вы git checkout какой-то другой филиал, вы замените предложение следующего коммита другим предложением, которое соответствует коммиту, который вы только чтопроверено.(Есть несколько исключений из этого правила, намеренно; см. Извлечение другой ветки, если в текущей ветке есть незафиксированные изменения .) Это, в свою очередь, означает, что индекс и рабочее дерево действительнопара: индекс индексы рабочее дерево.Когда вы вносите изменения в рабочее дерево, изменяя некоторые файлы, вам нужно обновить свой индекс, git add используя эти файлы.

Когда вы запускаете git commit, Git делает следующее:

  • сохранить свое имя и адрес электронной почты;
  • сохранить текущее время (отметку времени для нового коммита);
  • собрать сообщение от вас, чтобы войти вновый коммит;
  • использует текущий идентификатор хеша коммита в качестве родительского хеш-идентификатора;
  • сохраняет все это, а также файлы Git-ified в индексе, в новый коммит, который автоматически получает новый уникальный хэш-идентификатор хэша (путем вычисления криптографической контрольной суммы по всем этим данным)
  • записывает хэш-идентификатор нового коммита в текущая ветвь

То есть, если у вас было:

...--F--G--H   <-- master

, теперь у вас есть:

...--F--G--H--I   <-- master

name master теперь записывает хэш-идентификатор I нового коммита, который вы только что сделали.Этот новый коммит имеет в качестве parent хеш-идентификатор commit H, тот, который вы извлекли до того, как сделали новый коммит.

Это how historyсформирован!Создание нового коммита, который Git только что сделал из того, что находится в индексе прямо сейчас , когда вы запустили git commit, создает наш новый коммит I.Родитель нового коммита - это тот коммит, который вы сделали, чтобы Git проверил его.Поскольку Git сделал коммит из индекса, индекса и нового соответствия, так же, как они делали это при первом запуске git checkout master для получения коммита H.Теперь все выглядит хорошо для вас, чтобы изменить материал в рабочем дереве, использовать git add, чтобы скопировать его обратно в индекс, и запустить git commit, чтобы создать новый J, чей родитель I и чей сохраненный снимок приходитиз индекса.

Создание новой ветви

Теперь, когда вы знаете, как работают существующие ветви, давайте рассмотрим процесс создания новой ветви.Предположим, мы начинаем с коммита I, который вы только что сделали на master:

...--F--G--H--I   <-- master

Давайте создадим новую ветвь с именем feature/short:

git checkout -b feature/short

То, что мы теперь имеем, выглядитнапример:

...--F--G--H--I   <-- master, feature/short (HEAD)

То есть оба имени - оба master и feature/short - идентифицируют существующий коммит I.Специальное имя HEAD, которое Git использует, чтобы запомнить , в какой ветке мы находимся , присоединено к имени feature/short.

Теперь мы будем связываться с рабочим деревомкак обычно, запустите git add как обычно, и запустите git commit.Git соберет наше имя и адрес электронной почты, а также время, наше сообщение журнала и т. Д. И сделает новый коммит J со снимком из нашего индекса и с родителем I.Затем он запишет фактический хэш-идентификатор J, что бы это ни было, в имя feature/short:

...--F--G--H--I   <-- master
               \
                J   <-- feature/short (HEAD)

История , начинающаяся с J, возвращается к I, а затем H и так далее.Новый коммит находится на вершине новой ветви, feature/short.Наш индекс теперь соответствует как нашему коммиту J, так и нашему рабочему дереву, а HEAD остается присоединенным к нашей ветви feature/short.

Теперь вы знаете все, что нужно знать о ветвях - ну, кроме сирота веток, к которым мы скоро доберемся.

Добавление рабочих деревьев

Если вы уделяете пристальное внимание, вы уже поняли, что"index" не только индексирует рабочее дерево, но и оно и рабочее дерево также имеют тесную связь со специальным именем HEAD.Мы используем git checkout, чтобы присоединить наш HEAD к какому-либо имени ветви, и в процессе этого мы заполняем наш индекс и наше рабочее дерево всем из одного конкретного commit , того, что в tip этой ветви - коммит, на который указывает имя.Все эти объекты - HEAD, индекс, рабочее дерево и имя-ветви - изменяются одновременно.

Что делает git worktree add, так это создает новую тройку - новуюgroup - и запустите git checkout в этой новой группе.Новое рабочее дерево должно находиться в другой области на вашем компьютере: в другой папке, если вам нравится термин папка .Недавно добавленное рабочее дерево находится в другой ветви .Все рабочие деревья должны находиться в разных ветвях, даже если эти ветви names идентифицируют один и тот же коммит!Каждое рабочее дерево имеет свой собственный индекс и HEAD, и если вы переключаетесь с одного рабочего дерева на другое, вы должны изменить свое представление о HEAD и своем индексе.

Файлы внутри каждого коммита все лиофилизированы: Git-ified и сжаты и бесполезны.Файлы, извлеченные в рабочее дерево , повторно созданы и полезны.Таким образом, возможность добавлять больше рабочих деревьев означает, что вы можете иметь разные коммиты одновременно, если они находятся в разных рабочих деревьях.

(КакВ особом случае у любого рабочего дерева может быть отдельная HEAD , где вы извлекаете конкретный коммит по хеш-идентификатору. Поэтому, если вам нужно просмотреть шестнадцать различных исторических коммитов, вы можете добавить 16 рабочих деревьев, каждое нанапример, отдельную ГОЛОВУ этого исторического коммита.

Сиротские ветви

Теперь, когда у нас есть все это с пути, мы можем - наконец-то! - взглянуть на то, что сиротская ветвь есть.Это меньше, чем вы думаете!

Мы уже знаем, что HEAD обычно привязан к какому-либо существующему имени ветви, а существующие имена ветвей хранят хэш-идентификатор одного отдельного коммита, который мы называем tip этой ветви.Когда все настроено таким образом, выполнение нового коммита обновляет имя ветки , так что в существующем имени ветки теперь хранится новый уникальный идентификатор хеша коммита нового коммита, который мытолько что сделал.

Мы также мимоходом упомянули, что HEAD может вместо этого хранить хэш-идентификатор коммита - Git называет это detached HEAD .Здесь HEAD означает , а не , прикрепленное к названию ветви, отсюда и слово «отделенный».Здесь индекс и рабочее дерево работают обычным образом: в индексе хранятся все файлы из хеш-идентификатора коммитов detached-HEAD, в их форме для замораживания, но больше не заморожены, а в рабочем дереве хранятся все файлы.из этого коммита.Таким же способом вы можете сделать новый коммит: если вы это сделаете, Git просто сохранит хеш-код нового коммита в имя HEAD.Нет ответвление имя запоминает этот хэш-идентификатор.Только HEAD содержит этот хэш-идентификатор.Эти коммиты легко потерять по ошибке!Если вы используете git checkout для перемещения HEAD, вы потеряете хэш-идентификатор новых совершенных вами коммитов, так что будьте осторожны с отсоединенной головой, чтобы не потерять голову.: -)

Есть еще один режим для HEAD.Git позволяет вам прикрепить HEAD к имени ветки, которой не существует .Для этого вы используете git checkout --orphan:

git checkout --orphan feature/tall

Thisработает так же, как git checkout -b.Но -b first создает имя ветви , а , а затем присоединяет HEAD к имени ветви.Это создание имени ветви, которое хранит хеш-идентификатор внутри имени!Когда мы сделали feature/short выше, мы создали имя, указывающее на существующий коммит I, тот же коммит, который master уже запомнился.

Когда мы используем git checkout --orphan, Git не создает имя ветви .В итоге получается такая картина:

...--F--G--H--I   <-- master
               \
                J   <-- feature/short

feature/tall (HEAD)

Содержимое index и work-tree остаются неизменными, как и прежде, но имя feature/tall вообще не существует как имя ветви .Просто HEAD прикреплено к нему.Поскольку оно не существует как имя ветви, оно не указывает на какой-либо существующий коммит.

Если мы сделаем коммит прямо сейчас, Git сохранит в качестве нового снимка содержимое индекса.Если мы ничего не изменили, это содержимое соответствует commit J.Итак, мы получим новый коммит K.Родитель нового коммита K должен быть тем коммитом, который мы проверили прямо сейчас - идентифицированным по имени ветви, к которому присоединен наш HEAD.Но эта ветвь не существует!

Здесь Git делает то же самое, что и для самого первого коммита, который вы делаете в новом, абсолютно пустом репозитории, в котором еще нет коммитов.Git просто делает коммит с без родителей .Такой коммит называется корневым коммитом , и мы можем нарисовать его следующим образом:

K

Сделав новый коммит, Git теперь обновляет имя ветви, на которое наш HEADприлагается.Это имя feature/tall, поэтому теперь у нас есть:

...--F--G--H--I   <-- master
               \
                J   <-- feature/short

K   <-- feature/tall (HEAD)

Новая ветвь feature/tall теперь существует.Он возник , потому что мы сделали новый коммит - как всегда, из индекса - и этот новый коммит не имеет истории .

В конце концов, история - это просто цепочка коммитов, начинающаяся где угодно и работающая в обратном направленииМы начинаем с K и работаем задом наперед - ну, больше некуда идти.Итак, мы начинаем с K и показываем коммит, и все готово.Конец истории!Там больше ничего нет.

Теперь, конечно, если мы начнем с J или I и будем работать в обратном направлении, там будет история там .Но это не связано с историей, которую мы начинаем с K и работаем задом наперед.Так что feature/tall - это сиротская ветвь.Это просто не связанная со всем ветвь.

Это своеобразное свойство очень полезно в новом, абсолютно пустом хранилище.Такой репозиторий не имеет коммитов и веток, и самый первый коммит, который мы делаем - создавая некоторые файлы, копируя их в наш изначально пустой индекс и фиксируя - должен быть первым и единственным коммитом в thisвсе еще новое, но теперь не пустое хранилище.Если наш HEAD был присоединен к имени ветви master - что, конечно, было - это создает наше первое имя ветви, master, указывающее на первый и единственный коммит, который мы можем назватьA, но с уникальным хеш-идентификатором, который представляет собой криптографическую контрольную сумму содержимого файлов, которые мы создали, плюс наше имя, адрес электронной почты и введенное нами сообщение журнала, а также время , когда мы запустили git commit, все это в совокупности делает этот коммит уникальным в юниверсе.

Использование git checkout --orphan устанавливает аналогичные условия, за исключением того, что индекс и рабочее дерево, вероятно, не являются пустыми .Создание первого коммита для этой сиротской ветви - это то, что создает сиротскую ветвь.Входящий в состав снимок , как всегда, соответствует тому, что находится в index при запуске git commit.Сообщение журнала - это то, что вы вводите.У нового коммита нет родителя, поэтому Git называет его сиротой.

Заключение

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

0 голосов
/ 20 марта 2019

Вы ищете "git worktree" ...

  1. (опционально) создать пустой репозиторий; например,

    mkdir .repo/
    git clone --bare .../project .repo/project.git
    
  2. создать рабочие деревья из этого репозитория

    git -C .repo/project.git worktree add `pwd`/project-A branch-A
    git -C .repo/project.git worktree add `pwd`/project-B branch-B
    

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

...