много рецептов: https://webhamster.ru/mytetrashare/index/mtb0/

Ветвление и слияние веток

Клонируем репозиторий:

git clone https://....

Создаем ветку:

git branch new-branch

и переключаемся на нее:

git checkout new-branch

или обе команды в одной (создаем и сразу переключаемся на ветку):

git checkout -b new-branch

Увидеть какие ветки есть можно так:

git branch -a

Например - есть две ветки master и new-branch.
Когда говорят, что нужно слить ветку new-branch с веткой master, то это значит, что нужно выполнить переключение на ветку master:

git checkout master

и выполнить слияние ветки new-branch:

git merge new-branch

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

  1. Состояние первой ветки
  2. Состояние второй ветки
  3. Общее начальное состояние веток до изменений.

Включить отображение различий и общего начального стсоояния можно так:

git config merge.conflictstyle diff3

Допустим, была ветка master, от которой ответвились две ветки - develop и feature, в которых были произведены изменения в одном и том же файле.
При попытке слияния ветки feature с веткой develop:

git checkout develop
git merge feature

будет создан коммит, в котором зафиксирован конфликт двух веток:

Auto-merging main.go
CONFLICT (content): Merge conflict in main.go
Automatic merge failed; fix conflicts and then commit the result.

Увидеть суть конфликта можно в конце листинга указанного файла:

cat ./main.go
...
<<<<<<< HEAD
func sum (a, b int) int {
  return a + b
||||||| merged common ancestors
func sum (a, b int) int {
  t := a + b
  return t
=======
func sum(a, b int) int {
        t := a + b
        return t
>>>>>>> feature
}

Тут три фрагмента:

  • Первый принадлежит ветке develop:
func sum (a, b int) int {
  return a + b
}
  • Второй - принадлежит общему предку - коммиту в ветке master:
func sum (a, b int) int {
  t := b + a
  return t
}
  • Третий - ветке feature
func sum(a, b int) int {
        t := a + b
        return t
}

Теперь нужно как-то разрешить конфликт. Например, мы знаем, что нужно оставить самые последние версии (по времени внесения изменений).
Отлично написано тут: https://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/
Для разрешения конфликта будем использовать дефолтную утилиту vimdiff.
Настраиваем git для использования vimdiff в качестве mergetool:

git config merge.tool vimdiff
git config mergetool.prompt false

Приступаем к разрешению конфликта:

git mergetool

Листаем вниз и видим конфликт:

  func sum (a, b int) int {               |  func sum (a, b int) int {               |  func sum(a, b int) int {                
    return a + b                          |    t := a + b                            |          t := a + b                      
  ----------------------------------------|    return t                              |          return t                        
  ----------------------------------------|  ----------------------------------------|  ----------------------------------------
  ----------------------------------------|  ----------------------------------------|  ----------------------------------------
  ----------------------------------------|  ----------------------------------------|  ----------------------------------------
  ----------------------------------------|  ----------------------------------------|  ----------------------------------------
  ----------------------------------------|  ----------------------------------------|  ----------------------------------------
  ----------------------------------------|  ----------------------------------------|  ----------------------------------------
  ----------------------------------------|  ----------------------------------------|  ----------------------------------------
./main_LOCAL_25754.go   44,2           97% ./main_BASE_25754.go    45,2           97% ./main_REMOTE_25754.go  45,2           97%
  <<<<<<< HEAD                                                                                                                  
  func sum (a, b int) int {                                                                                                     
    return a + b                                                                                                                
  ||||||| merged common ancestors                                                                                               
  func sum (a, b int) int {                                                                                                     
    t := a + b                                                                                                                  
    return t                                                                                                                    
  =======                                                                                                                       
  func sum(a, b int) int {                                                                                                      
          t := a + b                                                                                                            
          return t                                                                                                              
main.go                                                                                                       47,8           95%

Тут мы видим:

  1. слева - состояние ветки в которую мерджим изменения (обозначены LOCAL)
  2. в середине - общий предок (BASE)
  3. справа - состояние ветки которую мы мерджим (REMOTE)
  4. Внизу - конфликт

Нужно оставить изменения, которые содержатся в ветке REMOTE. Для этого переключаемся между окошками с помощью Ctrl + w,j на нижнее окошко, листам вниз до конфликта и жмем последовательно:

Esc
:diffg RE
:wqa

Эта последовательность команд переводит редактор в режим команд (кнопочка Esc), затем применяем изменения из ветки REMOTE, а затем сохраняем изменения.
Измнения из LOCAL или BASE применяются соответственно командами:

diffg BA
diffg LO

И наконец можно выполнить commit:

git commit -a -m "Branch 'feature' merged to 'develop'"

Переключиться на нужный коммит

Находим нужный коммит с помощью

git log

И переключаемся на него:

git checkout <commit_hash>

Тегирование коммитов

Тегируем текущий коммит и пушим тег:

git tag <tag_name>
git push <remote_name> <branch_name> <tag_name>

Например:

git tag ops.07
git push origin master ops.07

Откат изменений

git reset --hard

Откат определенных коммитов (rebase & cherry-pick vs. revert): https://www.pixelstech.net/article/1549115148-git-reset-vs-git-revert

git revert ...

Объединение коммитов

https://git-scm.com/book/ru/v1/%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D1%8B-Git-%D0%9F%D0%B5%D1%80%D0%B5%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D1%8C-%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8
Объединить коммиты можно с помощью interactive rebase:

git rebase -i HEAD~3

Откроется тектовый редактор, в котором в начале будут перечислены коммиты (в данном случае - 3 штуки, начиная с самого старого) и применяемые к ним действия (по умолчанию - pick). Для того, чтобы объединить коммиты нужно для самого первого (старого) оставить pick, а для остальных прописать squash (или сокращенно s). В результате, изменения в коммитах объединятся в один (самый старый).

git reset `echo "Total Squash Commit" | git commit-tree HEAD^{tree}`

Предствьте себе, что случилась неприятность - вы недумая коммитили в master, в одном коммите могли оказаться изменения, касающиеся разных фич. И всё было хорошо, пока кто-то не захотел сделать ревью и увидел ХАОС. Что нужно - нужно разбить сделанные коммиты по веткам фич. Для этого - нужно разбить все старые коммиты и потом применить их к нужным веткам. Как разбить коммит, который зарыт глубоко в истории?? Поможет rebase! Что он делает - применяет коммиты последовательно к тому, на котором мы делаем rebase. Как вмешаться в этот процесс - запустить его в интерактивном режиме, когда мы можем указать что делать с каждым коммитом.
Допустим - у нас поверх последнего хорошего коммита выполнено уже 4 нехороших хаотичных. Первое - выполняем интерактивный ребейс:

git rebase -i HEAD~4

Либо прямо делаем rebase, указав хеш коммита:

git rebase -i <LAST_GOOD_COMMIT>

Откроется окно редактора, в котором будет список коммитов, начиная с самого старого. На против каждого будет стоять pick. Если сейчас ничего не менять - то ничего и не произойдет. Все коммиты применятся и мы откажемся на последнем. Но! Если для первого поставить вместо pick - edit, сохраним файлик и выйдем, то мы окажемся на этом коммите. А дальше - магия git. \ Отменяем коммит, на котором мы находимся в данный момент (который был закоммичен последним с нашей точки зрения - а это тот, для которого мы указали edit):

git reset HEAD~

Проверим, что изменения откатились и увидим список измененных в этом коммите файлов:

git status

Например, файлы разбиты по каталогам feature1 и feature2 (будем считать так для простоты). Нам надо разбить изменения в этих каталогах на два коммита. Последовательно добавляем в stage каталоги (или файлы) и коммитим:

git add ./feature1/*
git commit -m 'feature1 commit'
git add ./feature2/*
git commit -m 'feature2 commit'

В итоге наш крупный коммит был разбит на два. А теперь - продолжим rebase:

git rebase --continue

Всё.
Теперь эту процедуру можно повторить для остальных крупных беспорядочных коммитов.
А потом - ответвить от последнего хорошего коммита ветки и применять на эти ветки коммиты, соответствующие фичам:

git checkout <LAST_GOOD_COMMIT>
git checkout -b feature1
git cherry-pick <FEATURE1_SHA1>

и для второй фичи:

git checkout <LAST_GOOD_COMMIT>
git checkout -b feature2
git cherry-pick <FEATURE2_COMMIT_SHA1>

Скачать из репозитория git одну единственную папку

mkdir charts
cd charts
git init
git remote add origin https://github.com/helm/charts.git
git config core.sparseCheckout true
echo "stable/jenkins" > .git/info/sparse-checkout
git pull origin master

или

git archive --format tar --remote ssh://server.org/path/to/git HEAD docs/usage > /tmp/usage_docs.tar

Почистить репозиторий git

Допустим, у нас не было файлика .gitignore и в репозиторий попало много ненужного.
Теперь я хочу избавиться от ненужных файлов в репозитории.
Для этого:

  • делаем коммит текущего состояния, вместе с файликом .gitignore
  • удаляем все ненужные файлики из репы
  • удаляем все файлики из индекса репозитория ( отдельный файлик можно удалить так: git rm –cached foo.txt )
git rm -r --cached .
  • и добавляем все файлики обратно в индекс:
git add .
  • Коммитим состояние
git commit -m ".gitignore fix"
Enter your comment. Wiki syntax is allowed:
H N U V W
 
  • devops/git.txt
  • Last modified: 2020/05/28 21:33
  • by admin