reinitialized existing git repository in что делать
Получение «fatal: Not a git repository» при попытке удаленного добавления репозитория Git
Я представляю себя Git, следуя этому руководству:
все работает нормально до той части, где РЕПО добавляется к моей локальной машине:
(после замены имени пользователя, NFSNSERVER и REPOAME с правильными именами) я получаю сообщение об ошибке:
можете ли вы помочь мне пройти этот шаг?
22 ответов
вы ввели локальный репозиторий Git, в который предполагается добавить этот пульт?
вам нужно cd в репозиторий / рабочую копию, или вы не инициализировали или не клонировали репозиторий в первую очередь, в этом случае вам нужно инициализировать РЕПО в каталоге где вы хотите разместить РЕПО:
или клонировать репозиторий
Примечание: это не отвечает на общую проблему, которая была проблемой OP, но к другой проблеме, где это сообщение об ошибке может возникнуть. Мне не хотелось делать новый вопрос, чтобы записать этот ответ, скажите мне, Должен ли я сделать это вместо этого 😛
так как у меня не было ничего, что действительно нуждалось в сохранении, я просто пошел с dummy way и сделал.
по крайней мере для меня reflog ( git reflog ) исчезнуть полностью. Следовательно, если вы сделаете сброс, и были некоторые изменения, которые вы хотели предотвратить, я не уверен, что вы можете вернуть их после сброса. Поэтому убедитесь, что у вас есть все изменения, которые вы не можете потерять, в конечном счете, просто скопировав клон перед попыткой этого.
проблема пустой головной файл.
в командной строке / CLI вы получите эту ошибку, если ваш текущий каталог не является репозиторием. Итак, вы должны сначала CD в репо.
эта проблема возникла у меня после того, как я переместил местоположение проекта git в файловой системе. Когда я запускал некоторые команды git, произошла ошибка, например:
Я нашел в /home/rospasta/path_old/gitprojecta/.travis/.git был записан абсолютный путь к старому местоположению проекта. Вручную обновление этого пути нового местоположения решило проблему для меня.
поэтому моя проблема может быть или не быть проблемой git, но HTH.
затем снова инициализируйте git-РЕПО
просто введите следующее в cmd или git shell или любом другом терминале:
для этого вам нужно ввести одну команду, которая отсутствует в командах bitbucket
пожалуйста, попробуйте git init.
устранение:
даже у меня была такая же проблема. я написал сценарий оболочки, который будет создавать резервные копии всех моих кодов в моем РЕПО git в рабочие дни недели в 17: 55 с помощью crontab. увидев журналы cron, я нашел вышеупомянутую проблему.
и еще одна вещь на cmd перейдите в свой репозиторий git или cd, чтобы где ваш клон находится в вашем окне, обычно они будут храниться в документах под GitHub cd Document/Github / yourproject после этого вы можете иметь любые команды git
поэтому перейдите в каталог в каталог репозитория, где у вас есть проверка кода из git, а затем запустите эту команду.
GIT_DIR должен быть unset: unset GIT_DIR
в моем случае я обнаружил, что git в windows стал чувствительным к регистру для буквы диска с некоторого момента.
после обновления двоичного файла git в командах cli windows, которые раньше работали, остановились. например, путь в скрипте был D:\bla\file.txt, в то время как команда git принято только d:\bla\file.txt
все вернулось в норму, и git также распознал мои измененные / неустановленные файлы.
я исправил это, восстановив корневые файлы.
У меня была эта проблема с плагином Jenkins Git после проблем аутентификации с GitLab. Дженкинс докладывал о Хадсоне.подключаемый модуль.мерзавец.GitException: [. ]stderr: GitLab: проект, который вы искали, не найден. фатальная ошибка: не удалось прочитать из удаленного репозитория.’
однако, если я сделал «git clone» или «git fetch» непосредственно из окна Дженкинса (командной строки), он работал без проблем.
проблема была решена с помощью удаление весь /рабочая область каталог в папке Jenkins jobs для этого конкретного задания, например
git init new from existing repository
So I had an existing repository on github and I want to now push the same files into a new repository. I run git init in the terminal and it says «Reinitialized existing Git repository in. «
So how can I push the files into my new repo?
3 Answers 3
remove the old ‘origin’ remote and add new origin remote
Add all files and commit the changes
Now push the files.
But in fact you didn’t:
As you can see from this message, Git is «reinitializing» the existing repository. This basically does nothing at all. 1
Remember that a repository is not «a bunch of files», it is a collection of commits. More precisely, a repository consists, at its heart, of two databases:
The main database, which is copied by cloning, holds all of Git’s internal objects. This includes your commits as internal commit objects, along with three other object types that make the commit objects work in the way we expect.
The secondary database holds all of the names by which you can find commits. This includes branch names, tag names, remote-tracking names, and other names. This database is not copied, at least not directly, by cloning: the Git that is doing the cloning reads it, but modifies what is to be stored, so that the new clone of some existing repository normally has all the commits but none of the branches.
(At the end of the cloning process, the Git that is doing the cloning normally creates one new branch name in the new repository. The branch name that gets created here identifies the same commit as some name in the source repository. You choose which new branch name to create, if any, with arguments to your git clone command; if you don’t choose one, your Git picks a branch to create based on a suggestion made by the other Git, which looks at the source repository. GitHub and other hosting servers call this recommendation the default branch. They need a term for this because the way they set the recommendation is through their web interfaces. In fact, though, it’s simply whichever branch name HEAD in the source repository is attached-to.)
The phrase not in the repository here is doing some fairly heavy-duty work. What I mean is this:
1 Its main function is to copy a new hook template directory into place. If you have not adjusted your existing hooks, and are using the same standard hook template as before, nothing actually happens here. If you’re using a different template directory, this sort of «reinitializing» can be useful. I have not heard of anyone actually doing this, but it’s certainly possible that someone out there does it as a matter of routine.
3 Despite git clone not copying any of the branch names, the clone is usually just as good as a full copy, because the owner of the copy can now create a branch name for each of the remote-tracking names that they have in their clone. Those remote-tracking names are the result of copying your clone’s branch names. As soon as they make such names, they have all the same branches that you have.
If they like, they can copy each remote-tracking name to two branch names, after which they have twice as many branches as you have. This is the thing about branches in Git: they literally don’t matter. It’s not a matter of branch names. Those are not valuable currency, in Git. They’re just bookmarks, sort of. What matters in Git are the commits, and the clone has all of the commits. There are some exceptions to this rule, especially for shallow and/or single-branch clones, but in general, cloning copies all the commits and none of the branches, and that makes a complete copy, because branch names simply don’t matter.
Conclusion
Git: получение сообщения «фатальный: не репозиторий git» при попытке удаленного добавления репозитория Git
Я знакомлюсь с Git, следуя этому руководству:
Все работает нормально до тех пор, пока репо не будет добавлено на мою локальную машину:
(После замены USERNAME, NFSNSERVER и REPONAME на правильные имена) я получаю следующую ошибку:
Вы можете помочь мне пройти этот шаг?
30 ответов
Вы инициировали локальный репозиторий Git, в который предполагается добавить этот пульт?
Вам потребуется либо cd в репозиторий / рабочую копию, либо вы не инициализировали или не клонировали репозиторий в первую очередь, и в этом случае вам нужно инициализировать репо в каталоге, в котором вы хотите разместить репо:
Или клонировать репозиторий
ПРИМЕЧАНИЕ: это не ответ на общую проблему, которая была проблемой OP, а на другую проблему, при которой может появиться это сообщение об ошибке. Мне не хотелось задавать новый вопрос только для того, чтобы записать этот ответ, скажите мне, следует ли мне сделать это вместо этого: P
По крайней мере, для меня рефлог ( git reflog ) полностью исчез. Следовательно, если вы выполняете сброс и есть некоторые изменения, которые вы хотели предотвратить, я не уверен, что вы больше сможете их вернуть после сброса. Итак, убедитесь, что у вас есть все изменения, резервные копии которых вы не можете потерять, в конечном итоге просто скопировав клон, прежде чем пытаться это сделать.
Просто введите следующее в оболочке cmd, git или любом другом терминале:
Восстановление решило проблему.
Эта проблема возникла у меня после того, как я переместил проект git в файловую систему. Когда я запустил несколько команд git, произошла ошибка, например:
Я обнаружил, что в /home/rospasta/path_old/gitprojecta/.travis/.git был записан абсолютный путь к старому местоположению проекта. Обновление этого пути к новому местоположению вручную решило проблему для меня.
Так что моя проблема может быть или не быть проблемой git, но HTH.
В командной строке / CLI вы получите эту ошибку, если ваш текущий каталог НЕ является репозиторием. Итак, вы должны сначала поместить компакт-диск в репо.
Похоже, вы не собираетесь в свою конкретную папку. Например, если я работаю над проектом с именем bugsBunny, и он сохранен в папке d: / work: code, поэтому сначала вам нужно перейти в эту папку с помощью cd d: / work / code / bugsBunny, а затем вы можете продолжать использовать ваши команды git.
Для меня проблема возникает только тогда, когда кто-то пытается выполнить команды git из каталога, отличного от git ( т.е. из другого каталога, который не является рабочей копией ).
Решение:
Для этого вам нужно ввести одну команду, отсутствующую в командах bitbucket.
Пожалуйста, попробуйте git init.
GIT_DIR не должен быть установлен: unset GIT_DIR
И он снова заработал.
У меня была такая же проблема, когда я пробовал любые команды git (например, git status) с помощью Windows cmd. Итак, что я делаю, это после установки git для окна https://windows.github.com/ в среде переменных, добавьте путь к классу git в переменную «PATH». обычно git устанавливается на C:/user/»username»/appdata/local/git/bin и добавляет это в ПУТЬ в переменной окружения.
И еще одна вещь в cmd, перейдите в ваш репозиторий git или cd туда, где ваш клон находится в вашем окне, обычно они будут храниться в документах в github cd Document / Github / yourproject после этого вы можете использовать любые команды git
Поэтому перейдите в каталог в каталог репозитория, где вы получили код из git, а затем запустите эту команду.
В моем случае я обнаружил, что git в Windows в какой-то момент стал чувствительным к регистру букв диска.
После обновления бинарного файла git в Windows команды cli, которые раньше работали, перестали работать. например, путь в сценарии был D : \ bla \ file.txt, в то время как команда git принимала только d : \ bla \ file.txt
Все вернулось к норме, и git также распознал мои измененные / неустановленные файлы.
Я обратился к этому вопросу, потому что обнаружил сообщение об ошибке
И поскольку я боялся, что между версиями git есть некоторая несовместимость (к счастью, нет),
Ни один из ответов, которые я здесь прочитал, не был решением для моего случая, и я считаю некоторые из этих ответов опасными и вводящими в заблуждение.
В OpenBSD с ksh он работает с этим синтаксисом для определения псевдонимов, в то время как в Linux с bash
внутри такого же точного определения псевдонима не расширяется при вызове псевдонима, я решено удалением кавычек в определении псевдонима.
Исправил восстановлением корневых файлов.
У меня возникла эта проблема с плагином Jenkins Git после проблем с аутентификацией с GitLab. Дженкинс сообщал «hudson.plugins.git.GitException: [. ] stderr: GitLab: Проект, который вы искали, не найден. фатальный: не удалось прочитать из удаленного репозитория. ‘
Однако, если я сделал «git clone» или «git fetch» прямо из окна Jenkins (командная строка), он работал без проблем.
Проблема была решена путем удаления всего каталога / workspace в папке заданий Jenkins для этого конкретного задания, например
Это также может быть связано с разрешениями. Проверьте разрешения владельца / группы и убедитесь, что у вас есть соответствующие разрешения для доступа к этим данным. В моем случае я столкнулся с этой ошибкой при запуске «git status» в репо, владельцем которого был установлен root: root. Запуск «git status» от имени пользователя root решил мою проблему. В качестве альтернативы, если вы не хотите, чтобы владельцем пользователя / группы был root: root, выберите в репозитории то, к чему у вас есть доступ.
У меня была эта проблема, и я исправил ее, добавив файл README.md
В моем случае системный сбой привел к повреждению файла HEAD. В этом руководстве показано, как исправить эту и другие проблемы, с которыми вы можете столкнуться.
Нажмите git init с помощью терминала / cmd в нужной папке. Это сработает.
Затем снова инициализируйте репозиторий git
Чтобы узнать, какая версия поддерживается, перейдите в журналы actions/checkout и обновите свою версию server git до указанной версии или выше.
Ежедневная работа с Git
Я совсем не долго изучаю и использую git практически везде, где только можно. Однако, за это время я успел многому научиться и хочу поделиться своим опытом с сообществом.
Конечно, я попытаюсь рассказать обо всём по-порядку, начиная с основ. Поэтому, эта статья будет крайне полезна тем, кто только начинает или хочет разобраться с git. Более опытные читатели, возможно, найдут для себя что-то новое, укажут на ошибки или поделятся советом.
Вместо плана
Очень часто, для того чтобы с чем-то начать я изучаю целую кучу материалов, а это — разные люди, разные компании, разные подходы. Всё это требует много времени на анализ и на понимание того, подойдёт ли что-нибудь мне? Позже, когда приходит понимание, что универсальное решение отсутствует, появляются совершенно другие требования к системе контроля версий и к разработке.
Окружение
Если вы открываете консоль, пишите git и получаете вот это:
Перестаём бояться экспериментировать
Строго говоря, даже неудачный git push можно исправить.
Поэтому, спокойно можете клонировать любой репозиторий и начать изучение.
Строим репозитории
В первую очередь нужно понять что такое git-репозиторий? Ответ очень прост: это набор файлов. Папка `.git`. Важно понимать, что это только набор файлов и ничего больше. Раз 20 наблюдал проблему у коллег с авторизацией в github/gitlab. Думая, что это часть git-системы, они пытались искать проблему в конфигруации git, вызывать какие-то git-команды.
В частности, при клонировании вот так:
урл «превращается» в
Т.е. используется SSH и проблемы нужно искать в нём. Как правило, это неправильно настроенный или не найденный ssh-ключ. Гуглить надо в сторону «SSH Auth Key git» или, если совсем по взрослому, проверить, что же происходит:
Какие протоколы поддерживаются поможет справка (раздел GIT URLS):
Сделаем себе один (будет нашим главным тестовым репозиторием):
Итог: у нас есть 3 репозитория. Там ничего нет, зато они готовы к работе.
Начало GIT
Скандалы! Интриги! Расследования!
Как это всё работает?
Как это всё можно понять и запомнить?
Для этого нужно заглянуть под капот. Рассмотрим всё в общих чертах.
Git. Почти под капотом
Git сохраняет в commit содержимое всех файлов (делает слепки содержимого каждого файла и сохраняет в objects). Если файл не менялся, то будет использован старый object. Таким образом, в commit в виде новых объектов попадут только изменённые файлы, что позволит хорошо экономить место на диске и даст возможность быстро переключиться на любой commit.
Это позволяет понять, почему работают вот такие вот забавные штуки:
Да, не стоит хранить «тяжёлые» файлы, бинарники и прочее без явной необходимости. Они там останутся навсегда и будут в каждом клоне репозитория.
Каждый коммит может иметь несколько коммитов-предков и несколько дочерних-коммитов:
Мы можем переходить (восстанавливать любое состояние) в любую точку этого дерева, а точнее, графа. Для этого используется git checkout:
Каждое слияние двух и более коммитов в один — это merge (объединение двух и более наборов изменений).
Каждое разветвление — это появление нескольких вариантов изменений.
Кстати, тут хочется отметить, что нельзя сделать тэг на файл/папку, на часть проекта и т.д. Состояние восстанавливается только целиком. Поэтому, рекомендуется держать проекты в отдельном репозитории, а не складывать Project1, Project2 и т.д. просто в корень.
Теперь к веткам. Выше я написал:
В Git нет веток* (с небольшой оговоркой)
Получается, что так и есть: у нас есть много коммитов, которые образуют граф. Выбираем любой путь от parent-commit к любому child-commit и получаем состояние проекта на этот коммит. Чтобы коммит «запомнить» можно создать на него именованный указатель.
Такой именованный указатель и есть ветка (branch). Так же и с тэгом (tag). `HEAD` работает по такому же принципу — показывает, где мы есть сейчас. Новые коммиты являются продолжением текущей ветки (туда же куда и смотрит HEAD).
Указатели можно свободно перемещать на любой коммит, если это не tag. Tag для того и сделан, чтобы раз и навсегда запомнить коммит и никуда не двигаться. Но его можно удалить.
Вот, пожалуй, и всё, что нужно знать из теории на первое время при работе с git. Остальные вещи должны показаться теперь более понятными.
Терминология
index — область зафиксированных изменений, т.е. всё то, что вы подготовили к сохранению в репозиторий.
commit — изменения, отправленные в репозиторий.
HEAD — указатель на commit, в котором мы находимся.
master — имя ветки по-умолчанию, это тоже указатель на определённый коммит
origin — имя удалённого репозитория по умолчанию (можно дать другое)
checkout — взять из репозитория какое-либо его состояние.
Простые правки
Если вы сделали что-то не так, запутались, не знаете, что происходит — эти две команды вам помогут.
Вернёмся к нашим репозиториям, которые создали раньше. Далее обозначу, что один разработчик работает в dev1$, а второй в dev2$.
Поделимся со всеми. Но поскольку мы клонировали пустой репозиторий, то git по умолчанию не знает в какое место добавить коммит.
Он нам это подскажет:
Второй разработчик может получить эти изменения, сделав pull:
Добавим ещё пару изменений:
Посмотрим, что же мы сделали (запускаем gitk):
Выделил первый коммит. Переходя по-порядку, снизу вверх, мы можем посмотреть как изменялся репозиторий:
До сих пор мы добавляли коммиты в конец (там где master). Но мы можем добавить ещё один вариант README.md. Причём делать мы это можем из любой точки. Вот, например, последний коммит нам не нравится и мы пробуем другой вариант. Создадим в предыдущей точке указатель-ветку. Для этого через git log или gitk узнаем commit id. Затем, создадим ветку и переключимся на неё:
Выглядит это так:
Теперь нам понятно, как создаются ветки из любой точки и как изменяется их история.
Быстрая перемотка
Наверняка, вы уже встречали слова fast-forward, rebase, merge вместе. Настало время разобраться с этими понятиями. Я использую rebase, кто-то только merge. Тем «rebase vs merge» очень много. Авторы часто пытаются убедить, что их метод лучше и удобнее. Мы пойдём другим путём: поймём, что же это такое и как оно работает. Тогда сразу станет ясно, какой вариант использовать в каком случае.
Пока, в пределах одного репозитория, сделаем ветвление: создадим файл, положим в репозиторий, из новой точки создадим два варианта файла и попробуем объединить всё в master:
Создадим файл collider.init.sh с таким содержанием:
Добавим, закоммитим и начнём разработку в новой ветке:
Обратите внимание, что в имени ветки не запрещено использовать символ ‘/’, однако, надо быть осторожным, т.к. в файловой системе создаётся папка с именем до ‘/’. Если ветка с таким названием как и папка существует — будет конфликт на уровне файловой системы. Если уже есть ветка dev, то нельзя создать dev/test.
A если есть dev/test, то можно создавать dev/whatever, но нельзя просто dev.
Теперь, в каждой ветке напишем код, который, соответственно, будет запускать и уничтожать наш коллайдер. Последовательность действий приблизительно такая:
Разработка закончена и теперь надо отдать все изменения в master (там ведь старый коллайдер, который ничего не может). Объединение двух коммитов, как и говорилось выше — это merge. Но давайте подумаем, чем отличается ветка master от collider/start и как получить их объединение (сумму)? Например, можно взять общие коммиты этих веток, затем прибавить коммиты, относящиеся только к master, а затем прибавить коммиты, относящиеся только к collider/start. А что у нас? Общие коммиты — есть, только коммиты master — нет, только collider/start — есть. Т.е. объединение этих веток — это master + коммиты от collider/start. Но collider/start — это master + коммиты ветки collider/start! Тоже самое! Т.е. делать ничего не надо! Объединение веток — это и есть collider/start!
Ещё раз, только на буквах, надеюсь, что будет проще для восприятия:
master = C1 + C2 +C3
collider/start = master + C4 = C1 + C2 +C3 + C4
master + collider/start = Общие_коммиты(master, collider/start) + Только_у(master) + Только_у(collider/start) = (C1 + C2 +C3) + (NULL) + (C4) = C1 + C2 +C3 + C4
Как быстро узнать, что fast-forward возможен? Для этого достаточно посмотреть в gitk на две ветки, которые нужно объединить и ответить на один вопрос: существует ли прямой путь от ветки А к B, если двигаться только вверх (от нижней к верхней). Если да — то будет fast-forward.
В теории понятно, пробуем на практике, забираем изменения в master:
Результат (указатель просто передвинулся вперёд):
Объединение
Теперь забираем изменения из collider/terminate. Но, тот, кто дочитал до сюда (дочитал ведь, да?!) заметит, что прямого пути нет и так красиво мы уже не отделаемся. Попробуем git попросить fast-forward:
Что и следовало ожидать. Делаем просто merge:
Я даже рад, что у нас возник конфликт. Обычно, в этом момент некоторые теряются, лезут гуглить и спрашивают, что делать.
Для начала:
Конфликт возникает при попытке объединить два и более коммита, в которых в одной и той же строчке были сделаны изменения. И теперь git не знает что делать: то ли взять первый вариант, то ли второй, то ли старый оставить, то ли всё убрать.
Как всегда, две самые нужные команды нам спешат помочь:
Мы находимся в master, туда же указывает HEAD, туда же и добавляются наши коммиты.
Файл выглядит так:
Из-за того что изменения были в одних и тех же местах конфликтов получилось много. Поэтому, я брал везде первый вариант, а второй копировал сразу после первого, но с gui это сделать довольно просто. Вот результат — вверху варианты файла, внизу — объединение (простите за качество):
Сохраняем результат, закрываем окно, коммитим, смотрим результат:
Мы создали новый коммит, который является объединением двух других. Fast-forward не произошёл, потому, что не было прямого пути для этого, история стала выглядеть чуть-чуть запутаннее. Иногда, merge действительно нужен, но излишне запутанная история тоже ни к чему.
Вот пример реального проекта:
Конечно, такое никуда не годится! «Но разработка идёт параллельно и никакого fast-forward не будет» скажете вы? Выход есть!
Перестройка
Что же делать, чтобы история оставалась красивой и прямой? Можно взять нашу ветку и перестроить её на другую ветку! Т.е. указать ветке новое начало и воспроизвести все коммиты один за одним. Этот процесс и называется rebase. Наши коммиты станут продолжением той ветки, на которую мы их перестроим. Тогда история будет простой и линейной. И можно будет сделать fast-forward.
Другими словами: мы повторяем историю изменений с одной ветки на другой, как будто бы мы действительно брали другую ветку и заново проделывали эти же самые изменения.
Для начала отменим последние изменения. Проще всего вернуть указатель master назад, на предыдущее состояние. Создавая merge-commit, мы передвинули именно master, поэтому именно его нужно вернуть назад, желательно (а в отдельных случаях важно) на тот же самый commit, где он был.
Как результат, наш merge-commit останется без какого-либо указателя и не будет принадлежать ни к одной ветке.
Используя gitk или консоль перемещаем наш указатель. Поскольку, ветка collider/start уже указывает на наш коммит, нам не нужно искать его id, а мы можем использовать имя ветки (это будет одно и тоже):
Когда с коммита или с нескольких коммитов пропадает указатель (ветка), то коммит остаётся сам по себе. Git про него забывает, не показывает его в логах, в ветках и т.д. Но физически, коммит никуда не пропал. Он живёт себе в репозитории как невостребованная ячейка памяти без указателя и ждёт своего часа, когда git garbage collector её почистит.
Иногда бывает нужно вернуть коммит, который по ошибке был удалён. На помощь придёт git reflog. Он покажет всю историю, по каким коммитам вы ходили (как передвигался указатель HEAD). Используя вывод, можно найти id пропавшего коммита, сделать его checkout или создать на коммит указатель (ветку или тэг).
Выглядит это примерно так (история короткая, поместилась вся):
Посмотрим, что получилось:
Для того, чтобы перестроить одну ветку на другую, нужно найти их общее начало, потом взять коммиты перестраиваемой ветки и, в таком же порядке, применить их на основную (base) ветку. Очень наглядная картинка (feature перестраивается на master):
Важное замечание: после «перестройки» это уже будут новые коммиты. А старые никуда не пропали и не сдвинулись.
В теории разобрались, пробуем на практике. Переключаемся в collider/terminate и перестраиваем на тот коммит, куда указывает master (или collider/start, кому как удобнее). Команда дословно «взять текущую ветку и перестроить её на указанный коммит или ветку»:
Откроется редактор в котором будет приблизительно следующее:
Если коротко: то в процессе перестройки мы можем изменять комментарии к коммитам, редактировать сами коммиты, объединять их или вовсе пропускать. Т.е. можно переписать историю ветки до неузнаваемости. На данном этапе нам это не нужно, просто закрываем редактор и продолжаем. Как и в прошлый раз, конфликтов нам не избежать:
На данном этапе мы удаляли, редактировали, объединяли правки, а на выходе получили красивую линейную историю изменений:
Перерыв
Дальше примеры пойдут по сложнее. Я буду использовать простой скрипт, который будет в файл дописывать случайные строки.
На данном этапе нам не важно, какое содержимое, но было бы очень неплохо иметь много различных коммитов, а не один. Для наглядности.
Скрипт добавляет случайную строчку к файлу и делает git commit. Это повторяется несколько раз:
Передача и приём изменений
git remote
Как отмечалось выше, origin — это имя репозитория по умолчанию. Имена нужны, т.к. репозиториев может быть несколько и их нужно как-то различать. Например, у меня была копия репозитория на флешке и я добавил репозиторий flash. Таким образом я мог работать с двумя репозиториями одновременно: origin и flash.
Имя репозитория используется как префикс к имени ветки, чтоб можно было отличать свою ветку от чужой, например master и origin/master
В справке по git remote достаточно хорошо всё описано. Как и ожидается, там есть команды: add, rm, rename, show.
show покажет основные настройки репозитория:
Чтобы добавить существующий репозиторий используем add :
git fetch
Команда говорит сама за себя: получить изменения.
Стоит отметить, что локально никаких изменений не будет. Git не тронет рабочую копию, не тронет ветки и т.д.
Будут скачены новые коммиты, обновлены только удалённые (remote) ветки и тэги. Это полезно потому, что перед обновлением своего репозитория можно посмотреть все изменения, которые «пришли» к вам.
Ниже есть описание команды push, но сейчас нам нужно передать изменения в origin, чтобы наглядно показать как работает fetch:
Теперь от имени dev2 посмотрим, что есть и получим все изменения:
Выглядит это так:
Обратите внимание, что мы находимся в master.
Что можно сделать:
git checkout origin/master — переключиться на удалённый master, чтобы «пощупать» его. При этом нельзя эту ветку редактировать, но можно создать свою локальную и работать с ней.
git merge origin/master — объединить новые изменения со своими. Т.к. у нас локальных изменений не было, то merge превратится в fast-forward:
git pull
В 95% случаев, вам не нужно менять это поведение.
git push
На все вопросы может ответить расширенный вариант использования команды:
git push origin :
Примеры:
Включаемся в проект
К этому моменту уже более-менее понятно, как работает push, pull. Более смутно представляется в чём разница между merge и rebase. Совсем непонятно зачем это нужно и как применять.
Когда кого-нибудь спрашивают:
— Зачем нужна система контроля версий?
Чаще всего в ответе можно услышать:
— Эта система помогает хранить все изменения в проекте, чтобы ничего не потерялось и всегда можно было «откатиться назад».
А теперь задайте себе вопрос: «как часто приходится откатываться назад?» Часто ли вам нужно хранить больше, чем последнее состояние проекта? Честный ответ будет: «очень редко». Я этот вопрос поднимаю, чтобы выделить гораздо более важную роль системы контроля версий в проекте:
Система контроля версий позволяет вести совместную работу над проектом более, чем одному разработчику.
То, на сколько вам удобно работать с проектом совместно с другими разработчиками и то, на сколько система контроля версий вам помогает в этом — самое важное.
Используете %VCS_NAME%? Удобно? Не ограничивает процесс разработки и легко адаптируется под требования? Быстро? Значит эта %VCS_NAME% подходит для вашего проекта лучше всего. Пожалуй, вам не нужно ничего менять.
Типичные сценарии при работе над проектом
Чтобы добиться такого процесса, нужно чётко определиться где и что будет хранится. Т.к. ветки легковесные (т.е. не тратят ресурсов, места и т.д.) под все задачи можно создать отдельные ветки. Это является хорошей практикой. Такой подход даёт возможность легко оперировать наборами изменений, включать их в различные ветки или полностью исключать неудачные варианты.
Исправление багов
Создадим ветку dev и рассмотрим типичный сценарий исправления багов.
И второй разработчик «забирает» новую ветку к себе:
Пусть 2 разработчика ведут работу каждый над своим багом и делают несколько коммитов (пусть вас не смущает, что я таким некрасивым способом генерирую множество случайных коммитов):
Когда работа закончена, передаём изменения в репозиторий. Dev1:
На первый взгляд, кажется, что здесь много действий и всё как-то сложно. Но, если разобраться, то описание того, что нужно сделать намного больше, чем самой работы. И самое главное, мы уже так делали выше. Приступим к практике:
Посмотрим на результат:
Теперь, всё как положено: наши изменения в dev и готовы быть переданы в origin.
Передаём:
Нам откроется редактор и даст возможность исправить нашу историю, например, поменять порядок коммитов или объединить несколько. Об этом писалось выше. Я оставлю как есть:
После того как все конфликты решены, история будет линейной и логичной. Для наглядности я поменял комментарии (интерактивный rebase даёт эту возможность):
Теперь наша ветка продолжает origin/dev и мы можешь отдать наши изменения: актуальные, адаптированные под новые коммиты:
Далее история повторяется. Для удобства или в случае работы над несколькими багами, как говорилось выше, удобно перед началом работы создать отдельные ветки из dev или origin/dev.
Feature branch
Бывает так, что нужно сделать какой-то большой кусок работы, который не должен попадать в основную версию пока не будет закончен. Над такими ветками могут работать несколько разработчиков. Очень серьёзный и объёмный баг может рассматриваться с точки зрения процесса работы в git как фича — отдельная ветка, над которой работают несколько человек. Сам же процесс точно такой же как и при обычном исправлении багов. Только работа идёт не с dev, а с feature/name веткой.
Вообще, такие ветки могут быть коротко-живущими (1-2 неделя) и долго-живущими (месяц и более). Разумеется, чем дольше живёт ветка, тем чаще её нужно обновлять, «подтягивая» в неё изменения из основной ветки. Чем больше ветка, тем, вероятно, больше будет накладных расходов по её сопровождению.
Начнём с коротко-живущих (short-live feature branches)
Обычно ветка создаётся с самого последнего кода, в нашем случае с ветки dev:
Теперь работу на feature1 можно вести в ветке feature/feature1. Спустя некоторое время в нашей ветке будет много коммитов, и работа над feature1 будет закончена. При этом в dev тоже будет много изменений. И наша задача отдать наши изменения в dev.
Выглядеть это будет приблизительно так:
Ситуация напоминает предыдущую: две ветки, одну нужно объединить с другой и передать в репозиторий. Единственное отличие, это две публичные (удалённые) ветки, а не локальные. Это требует небольшой коммуникации. Когда работа закончена, один разработчик должен предупредить другого, что он собирается «слить» изменения в dev и, например, удалить ветку. Таким образом передавать в эту ветку что-либо будет бессмысленно.
А дальше алгоритм почти такой как и был:
Картинка, feature1 стала частью dev, то что на и нужно было:
Делаем push и чистим за собой ненужное:
Долго-живущие ветки (long-live feature branches)
Сложность долго-живущих веток в их поддержке и актуализации. Если делать всё как описано выше то, вероятно быть беде: время идёт, основная ветка меняется, проект меняется, а ваша фича основана на очень старом варианте. Когда настанет время выпустить фичу, она будет настолько выбиваться из проекта, что объединение может быть очень тяжёлым или, даже, невозможным. Именно поэтому, ветку нужно обновлять. Раз dev уходит вперёд, то мы будем просто время от времени перестраивать нашу ветку на dev.
Всё бы хорошо, но только нельзя просто так взять и перестроить публичную ветку: ветка после ребэйза — это уже новый набор коммитов, совершенно другая история. Она не продолжает то, что уже было. Git не примет такие изменения: два разных пути, нет fast-forward’а. Чтобы переписать историю разработчикам нужно договориться.
Кто-то будет переписывать историю и принудительно выкладывать новый вариант, а в этот момент остальные не должны передавать свои изменения в текущую ветку, т.к. она будет перезаписана и всё пропадёт. Когда первый разработчик закончит, все остальные перенесут свои коммиты, которые они успеют сделать во время переписи уже на новую ветку. Кто говорил, что нельзя ребэйзить публичные ветки?
Приступим к практике:
Второй разработчик подключается к работе всё стандартно:
Добавим ещё несколько коммитов в основную ветку dev и история будет такая:
Настало время актуализировать feature/long, но при этом разработка должна продолжиться отдельно. Пусть перестраивать будет dev1. Тогда он предупреждает dev2 об этом и начинает:
В это время dev2 продолжает работать, но знает, что ему нельзя делать push, т.к. нужной ветки ещё нет (а текущая будет удалена).
Первый заканчивает rebase и история будет такой:
Ветка перестроена, а origin/feature/long остался там, где и был. Цели мы достигли, теперь нужно поделиться со всеми:
Git лишний раз напоминает, что что-то не так. Но теперь, мы точно знаем, что мы делаем, и знаем, что так надо:
Теперь можно работать дальше предупредив остальных об окончании работ.
Посмотрим, как эти изменения отразились на окружающих и на dev2 в частности:
N, чтобы указать на предыдущий N-ый коммит.
HEAD
1 — это предыдущий, а HEAD
2 — это предпредыдущий. HEAD
5 — 5 коммитов назад. Удобно, чтобы не запоминать id.
Посмотрим, как теперь нам перестроить только один коммит:
1
Если бы нам надо было перетянуть 4 коммита, то было бы feature/long
Осталось продолжить работу.
Hotfixes
Hotfixы нужны, т.к. бывают очень критичные баги, которые нужно исправить как можно быстрее. При этом нельзя передать вместе с hotfix-ом последний код, т.к. он не оттестирован и на полное тестирование нет времени. Нужно только это исправление, максимально быстро и оттестированное. Чтобы это сделать, достаточно взять последний релиз, который был отправлен на продакшен. Это тот, где у вас остался tag или master. Тэги играют очень важную роль, помогая понять что именно было собрано и куда это попало.
Тэги делаются командой
Всякие полезности
Git позволяет настраивать aliasы для различных команд. Это бывает очень удобно и сокращает время набора команд. Приводить здесь примеры не буду, в Интернете их полно, просто поищите.
Перед тем, как отдавать свои изменения их можно перестроить на самих себя, чтобы привести историю в порядок.
Например, в логах может быть такое:
Перестроим сами себя, но начиная с 6 коммитов назад:
Теперь в интерактивном режиме можно объединить (squash) первые два коммита в один и последние четыре. Тогда история будет выглядеть так:
Такое намного приятнее передавать в общим репозиторий.
Выводы
Причемания и апдейты
gcc рекомендовал посмотреть на Git Extensions.
borNfree подсказал ещё один GUI клиент Source Tree.
zloylos поделился ссылкой на визуализатор для git
olancheg предложил посмотреть на ещё один туториал для новичков.
PS. Что тут обычно пишут, когда первый пост на хабре? Прошу не судить строго, писал как мог.