Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Last revisionBoth sides next revision
devops:git [2020/03/27 14:22] – [Откат изменений] admindevops:git [2021/04/20 11:45] – [Разрешение всех конфликтов слияния в пользу текущего или входящего состояния] admin
Line 1: Line 1:
 +много рецептов: 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
 +===== Разрешение конфликтов при слиянии =====
 +Конфликт возникает, если изменения в одном и том же файле были произведены в двух ветках и мы пытаемся слить эти ветки. \\
 +Для того, чтобы увидеть суть конфиликта нужно видеть три состояния:
 +  - Состояние первой ветки
 +  - Состояние второй ветки
 +  - Общее начальное состояние веток до изменений.
 +Включить отображение различий и общего начального стсоояния можно так:
 +  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**:
 +<code>func sum (a, b int) int {
 +  return a + b
 +}</code>
 +  * Второй - принадлежит общему предку - коммиту в ветке **master**:
 +<code>func sum (a, b int) int {
 +  t := b + a
 +  return t
 +}
 +</code>
 +  * Третий - ветке **feature**
 +<code>func sum(a, b int) int {
 +        t := a + b
 +        return t
 +}
 +</code>
 +Теперь нужно как-то разрешить конфликт. Например, мы знаем, что нужно оставить самые последние версии (по времени внесения изменений). \\
 +Отлично написано тут: https://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/ \\
 +Для разрешения конфликта будем использовать дефолтную утилиту **vimdiff**. \\
 +Настраиваем git для использования **vimdiff** в качестве **mergetool**:
 +<code>
 +git config merge.tool vimdiff
 +git config mergetool.prompt false
 +</code>
 +Приступаем к разрешению конфликта:
 +  git mergetool
 +Листаем вниз и видим конфликт:
 +<code>  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,          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,          95%</code>
 +Тут мы видим:
 +  - слева - состояние ветки в которую мерджим изменения (обозначены **LOCAL**)
 +  - в середине - общий предок (**BASE**)
 +  - справа - состояние ветки которую мы мерджим (**REMOTE**)
 +  - Внизу - конфликт
 +Нужно оставить изменения, которые содержатся в ветке **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'"
 +  
 +===== Разрешение всех конфликтов слияния в пользу текущего или входящего состояния =====
 +Если при merge возникли конфиликты, то можно одной командой разрешить все конфликты в ту или другую пользу. \\
  
 +Оставить все конфликтные участки в текущем состоянии:
 +  git checkout --ours .
 +Применить во всех случаях конфликтов входящие изменения:
 +  git checkout --theirs .
 +====== Переключиться на нужный коммит ======
 +Находим нужный коммит с помощью 
 +  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 одну единственную папку ======
 +<code>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</code>
 +или
 +  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 )
 +<code>git rm -r --cached .</code>
 +  * и добавляем все файлики обратно в индекс:
 +<code>git add .</code>
 +  * Коммитим состояние
 +<code>git commit -m ".gitignore fix"</code>
 +
 +====== No url found for submodule path ======
 +Иногда в проект попадают подпроекты, которые сами по себе являются репозиториями git. В этому случае они становятся **submodules** и **git** работает с ними как со ссылками на внешние репы. То есть он не сохраняет в репозитории файлы этих проектов, а скачивает их по требованию. \\
 +Однако, может так статься, что файлы подпроекта становятся частью проекта, а ссылки на внешние репозитории изменяются или перестают работать. \\
 +Если ссылка на внешний репозитрий изменилать, то следует поправить ее в файле **.gitmodules** в корне проекта:
 +<code>[submodule "path_to_submodule"
 +  path = path_to_submodule 
 +  url = git://url-of-source/</code>
 +А если подпроект нужно просто сделать частью проекта, то удаляем его так:
 +  git rm --cached path_to_submodule
 +  
 +====== fatal: refusing to merge unrelated histories ======
 +Иногда, после манипуляций с историей коммитов нарушается связность историй веток и при попытке выполнения
 +  git merge BRANCHNAME
 +Появляется сообщение вида:
 +  fatal: refusing to merge unrelated histories
 +В этом случае - если нам нужно заменить содержимое одной ветки, содержимым другой, то в ветке куда делаем merge выполняем:
 +  git pull origin BRANCHNAME -X theirs --allow-unrelated-histories
 +То есть - подтягиваем в текущую ветку все изменения из ветки репозитория **BRANCHNAME**, разрешаем нарушение истории коммитов (**--allow-unrelated-histories**) и автоматически разрешаем все конфликты в пользу применения входящих изменений (**-X theirs**)
  • devops/git.txt
  • Last modified: 2021/12/01 09:01
  • by admin