Git: вытащить файлы с удаленного компьютера, но не добавлять в .git / метаданные - PullRequest
1 голос
/ 02 октября 2019
  • У меня есть удаленный репозиторий "shared_stuff" и репозиторий "project_a".
  • структура каталогов одинакова в обоих репозиториях, но содержимое отличается
  • Время от времени я хочу перенести изменения из "shared_stuff" в "project_a".
  • Все изменения в "shared_stuff" должны перезаписывать в конечном итоге уже существующие файлы в "project_a"
  • НО изменения / новые файлы не должны добавляться в "метаданные git" для project_a, т.е. они не должны становиться частью "project_a"
  • без git. Я бы скопировал все файлы из "shared_stuff" в "project_a", переписав все в project_a

Есть ли элегантное решение с помощью git? Поддерево oder Подмодуль не работает, насколько я вижу.

Редактировать: Уточнение - изменения НЕ ДОЛЖНЫ добавляться в индекс project_a, я никогда не хочу фиксировать их в project_a. Напротив, я добавлю файл gitignore в project_a, чтобы git игнорировал изменения, которые я извлек из shared_stuff (я могу узнать их с помощью соглашений об именах)

Ответы [ 2 ]

1 голос
/ 02 октября 2019

Если у вас есть два пульта для репозиториев project_a и shared_stuff, то вы можете создать локальное отделение для shared_stuff репо и извлекать изменения только в локальное отделение: git branch shared_stuff-master shared_stuff/master (я полагаю, вы будете использовать masterветка, замени ее если нет). Затем вы можете [squah merge] совместно использовать ветку в первичную ветку и переместить ее в project_a:

git checkout -b shared_stuff-master shared_stuff/master
git pull shared_stuff master
git checkout -b master project_a/master
git merge --no-commit --squash shared_stuff-master
git commit -m "merged changes from shared_stuff"
git push project_a master

Так что вы не будете публиковать ссылки из shared_stuff репо в project_a репо, только измененные файлыбудет опубликовано.

0 голосов
/ 02 октября 2019

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

Основы закулисной работы

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

Эти хеш-идентификаторы, хранящиеся в каждом коммите и указывающие назад на некоторые более ранние коммиты,сформировать историю в репозитории Git. То есть, если вы только что сделали новый коммит в своем репозитории, в своей ветке master, этот новый коммит имеет какой-то большой уродливый хеш-идентификатор, уникальный для него. Давайте назовем этот хэш-идентификатор H. Внутри самого коммита H есть строка с надписью «Мой родительский коммит ______»: заполните пустое значение хеш-идентификатора коммита, который вы извлекли как ваш master до того, как вы сделали новый коммит, нодавайте просто для удобства вызовем этот коммит G. Коммит G, конечно, будет иметь еще один хэш-идентификатор родительского коммита: назовем это F. Если каждый коммит находится в красивой аккуратной простой линии (нет веток и слияний), и мы рисуем их, мы получаем:

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

, где H - это последний коммитв этой строке. Он указывает на своего родителя G, который указывает на F, и так далее, вплоть до самого первого сделанного когда-либо коммита, который мы можем назвать A. Поскольку это первый коммит, он не может и, следовательно, не имеет более раннего коммита: он указывает ни на что другое и называется корневым коммитом .

Для find , который является последним , Git хранит необработанный хэш-идентификатор последнего коммита под именем ветви, как master. Таким образом, имя master просто указывает на фиксацию H сейчас:

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

Буквально минуту назад, перед тем как вы сделали коммит H, имя master указывало на фиксацию H. При создании нового коммита хранится:

  • новый снимок,
  • с вами в качестве автора и коммиттера,
  • "сейчас" как две даты-и отметки времени,
  • ваше сообщение журнала в качестве сообщения журнала;и
  • предыдущий branch-tip-commit в качестве его родителя.

content в новом снимке происходит из индекса. Сохранение индекса не составляет различий: это следующий моментальный снимок фиксации, готовый к выполнению.

Когда вы запускаете git status, Git запускает git diffвнутренне, чтобы увидеть, что отличается в следующем коммите, готовом к выполнению, по сравнению с текущим коммитом. Все, что отличается здесь, указано как "изменения, которые необходимо зафиксировать", но они не изменения , это целые файлы. В то же время, Git также запускает второй git diff, чтобы сравнить файлы индекса с файлами рабочего дерева. Что бы ни было отличается здесь указано как "изменения, не подготовленные для фиксации", но они не изменения , это целые файлы. Файлы рабочего дерева находятся только в форме, которую вы можете видеть и работать с ней. (Индексные копии находятся в специальной форме Git-only, готовой для фиксации.)

Yoу вас есть один "главный" другой Git, который вы называете origin. С этим Git вы вызываете его - или, скорее, ваш Git вызывает его - время от времени, и ваш Git сравнивает хеш-идентификаторы с их Git. Если у вашего Git есть хеш-идентификаторы, которых у них нет, ваш Git может дать своему Git эти новые коммиты. Затем, независимо от того, дали ли вы им новые коммиты, ваш Git может попросить их, например, . Пожалуйста, установите master, чтобы указать H. (Чтобы это работало, вы должны сначала отправить им коммит H.) Они либо согласны, и теперь у них есть новый коммит H, и их master также указывает (их копия) коммитаH, или они говорят: «Нет, я не буду этого делать ______» (заполните бланк с указанием причины).

Как получить то, что вы хотите

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

Итак, после использования git remote add для добавления имени и URL-адреса для этого третьего Git, вы можете запустить git fetch <em>name</em> - использовать имя, которое вы использовали вgit remote add команда - для получения всех коммитов из их Git. Здесь я буду использовать имя third (хотя это не очень хорошее имя). Для простоты предположим, что у вас есть всего восемь коммитов, которые мы назовем от A до H, и что у них всего три коммита, которые мы назовем N, O и P;и что у вас есть одно имя ветви, master, и они тоже. Ваш Git получит три своих коммита, так что теперь у вас есть:

A--B--C--D--E--F--G--H   <-- master

N--O--P

Обратите внимание, что ни один из их коммитов не соединяется ни с одним из ваших, и наоборот: это не связанные истории. Теперь, когда у вас есть три коммита, ваш Git должен создать имя , чтобы помнить, что third master указывает на коммит P. Ваш Git будет использовать имя third/master (или любое другое имя, которое вы использовали), поэтому теперь у вас есть:

A--B--C--D--E--F--G--H   <-- master, origin/master

N--O--P   <-- third/master

Я добавил origin/master, потому что ваш Git предположительно помнит, что origin 'master указывает на фиксацию H, поэтому ваш Git теперь имеет два из этих имен для удаленного слежения: origin/master, помня H и third/master, помня P.

Проблема в том, что вы хотите, чтобы (некоторые) файлы выходили из коммита P. Если вы просто выполните:

git checkout third/master

Git переведет вас в режим «отделяемого HEAD» после:

  • удаления всех ваших файлов из индекса ирабочее дерево и
  • копирование всех файлов из коммита P в индекс и рабочее дерево

, что не оставляет вас нигде из ваших файлов, где вы можете работать с ними,Затем вы можете:

git checkout master

, чтобы ваш Git удалил из индекса все файлы P, удалил их все из рабочего дерева и повторно заполнил индекс и ваше рабочее дерево из *Файлы 1170 *.

Это не сильно помогает. Конечно, все возвращается к тому, как вам нужно работать с вашими вещами, но вы не можете видеть их файлы! Они вернулись, чтобы быть скрытыми внутри коммита P. Хотя есть несколько вещей, которые вы можете сделать здесь, и самый прямой из них:

git checkout third/master -- <paths>

whПеред тем как аргументы <paths> сообщают Git, какие файлы следует извлечь из коммита P. Эта форма git checkout не меняет ваш текущий коммит , поэтому вы все равно получите master и, следовательно, коммит H, извлечены. Вместо этого он извлекает именованные файлы, включая целые каталоги, полные файлов, если <paths> называет какой-либо каталог или набор каталогов: все файлы в этом поддереве будут извлечены - с содержимым этих файлов. в и ваш индекс и ваше рабочее дерево.

Итак, если все, что вы хотите от commit P, это, скажем, foo/, просто используйте:

git rm -rf foo/
git checkout third/master -- foo/

и вы готовы зафиксировать обновленные файлы: все в foo/ только что вышло из фиксации P. Шаг git rm -rf foo/ говорит Git удалить все ваши файлы, которые вам нужны, только если в вашем foo/ есть файлы, которые не находятся в коммите P '* foo/ (ивы хотите, чтобы эти файлы пропали).

Если вам нужно быть более избирательным - если foo/ недостаточно, вы можете:

  • использовать git worktree addчтобы создать новое рабочее дерево, содержащее коммит P или
  • , вообще не использовать этот удаленный метод third: просто клонировать их хранилище в каком-то другом месте и выборочно копировать файлы из этого клона в ваш клонзатем git add скопированные файлы.

Обратите внимание, что если вы используете отдельный (четвертый!) клон, становится невозможным случайно передать цепочку коммитов N-O-P из вашего Git в ваш Git. origin, поскольку они никогда не существуют нигде в вашем Git. Если вы добавите пульт с именем third, эти дополнительные коммиты все еще не перейдут к origin, если вы не выполните одно из следующих двух действий: 1

  • заставить некоторые из ваших собственных веток использовать эти коммиты (напрямую, путем указания на них или косвенно, путем слияния несвязанной истории в вашу собственную историю коммитов), или
  • установить один или несколько вашихсобственные локальные имена ветвей, указывающие на их коммиты

и , затем git push эти локальные имена ветвей origin. То есть вы должны сначала поместить N-O-P (или любые другие сторонние коммиты Git-репо) в свою собственную историю, а затем попросить Git отправить свою историю - ваши коммиты - origin ипопросите их Git запомнить один из этих новых коммитов под некоторым именем (обычно с именем ветки, но достаточно любого имени - ветви, тега и т. д.).


1 Вы можететакже намеренно отправьте всю цепочку N-O-P, выполнив:

git push origin <hash-of-P>:<name>

, где <name> - это имя, которое вы хотите, чтобы их (origin) Git установили после получения цепочки N-O-P. Но, вероятно, вы этого не сделаете ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...