git rebase squash портит историю веток - PullRequest
2 голосов
/ 13 мая 2019

У меня есть git хранилище с длинной историей и различными ветками, которые были объединены в master. Однако первые коммиты просто происходят от первого, без каких-либо ветвлений.

Я бы хотел объединить некоторые из этих первых коммитов. Для этого я сделал

git rebase -i --root

затем в редакторе я выбрал коммиты для сквоша, затем изменил сообщение о коммите для квадратичных коммитов. git rebase, кажется, работает нормально, но, к моему удивлению, git log показывает, что история была испорчена, теряя историю слияния ветвей: несколько коммитов теперь кажутся дублирующими, а ветви не объединяются.

Хотя трудно воспроизвести полную проблему, здесь я приведу минимальный пример, демонстрирующий аналогичную проблему. Рассмотрим локальный репозиторий git, созданный следующими командами

git init .
touch foo.txt
git add foo.txt
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done
git checkout -B test
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt; git commit -a -m test$i; done
git checkout master
git merge test
for (( i=4; i<5; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done

Это создает хранилище, после того как некоторые фиксируют новую ветку test, которая затем объединяется в master (простой перемотка вперед) и, наконец, последний коммит на master. Журнал репозитория (Автор / Дата удалена)

git log --graph --all

* commit ba326e1ada9525fd2b1b275c597ad189b7cf3ddf (HEAD -> master)
| Author: 
| Date:   
| 
|     4
| 
* commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| Author: 
| Date:   
| 
|     test3
| 
* commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| Author: 
| Date:   
| 
|     test2
| 
* commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| Author: 
| Date:   
| 
|     test1
| 
* commit 2420498ae794fa900dd9fee8296b7561e08028b8
| Author: 
| Date:   
| 
|     3
| 
* commit 2d43d89d2b75566f37a99b7cce294cddcd3fbf7a
| Author: 
| Date:   
| 
|     2
| 
* commit 487e5158e461479f848eb7313dc87090f197f4a7
  Author: 
  Date:   

      1

Теперь я объединяю коммиты "2" и "3" в один. Я делаю

git rebase -i --root

и в редактор ввожу

pick 487e515 1
pick 2d43d89 2
squash 2420498 3
pick dd67e3c test1
pick 303e95f test2
pick b739356 test3
pick ba326e1 4

В сообщении о коммите для сдавленного коммита я ввожу "squash 2 3" git rebase кажется, чтобы хорошо закончить:

[detached HEAD b5d1682] squash 2 3
 Date: Mon May 13 12:01:19 2019 +0200
 1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/master.

Однако, к моему удивлению, история теперь беспорядок

git log --graph --all

* commit 463cbb99f553d555fadbe05a3cc90aeb46b0bbce  (HEAD -> master)
| Author: 
| Date:   
| 
|     4
| 
* commit 194e78fc74d4eaea62a8b903baa6379dfc8578d3
| Author: 
| Date:   
| 
|     test3
| 
* commit 8f5e0be6dd8288790b2893e9b3fa97e5e1021134
| Author: 
| Date:   
| 
|     test2
| 
* commit 1060aea0f3da5dfee8e37adc68d82d77e7c02ba4
| Author: 
| Date:   
| 
|     test1
| 
* commit b5d168283be820fed3b9862576c44c054402ab50
| Author: 
| Date:   
| 
|     squash 2 3
|   
| * commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| | Author: 
| | Date:   
| | 
| |     test3
| | 
| * commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| | Author: 
| | Date:   
| |
| |     test2
| | 
| * commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| | Author: 
| | Date:   
| | 
| |     test1
| | 
| * commit 2420498ae794fa900dd9fee8296b7561e08028b8
| | Author: 
| | Date:   
| | 
| |     3
| | 
| * commit 2d43d89d2b75566f37a99b7cce294cddcd3fbf7a
|/  Author: 
|   Date:  
|   
|       2
| 
* commit 487e5158e461479f848eb7313dc87090f197f4a7
  Author: 
  Date:   

      1

git rebase снова разделил объединенную ветвь test, в то время как ветвь master сохраняет историю. Однако я также потерял информацию о том, что test было объединено в master.

Я ожидал такую ​​историю:

git log --graph --all

* commit ba326e1ada9525fd2b1b275c597ad189b7cf3ddf (HEAD -> master)
| Author: 
| Date:   
| 
|     4
| 
* commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| Author: 
| Date:   
| 
|     test3
| 
* commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| Author: 
| Date:   
| 
|     test2
| 
* commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| Author: 
| Date:   
| 
|     test1
| 
* commit 2420498ae794fa900dd9fee8296b7561e08028b8
| Author: 
| Date:   
| 
|     squash 2 3
|
* commit 487e5158e461479f848eb7313dc87090f197f4a7
  Author: 
  Date:   

      1

Это ошибка git rebase? Я использую GIT 2.19.2

Если я попытаюсь без ветки test или если удаляю ветку test перед ребазингом, она работает нормально и выдает желаемую историю, но все равно я теряю информацию о ветке test.

Более того, в более сложных случаях, когда test объединяется без простой быстрой перемотки вперед, удаление ветви test перед сквошем с git rebase приводит к полной потере истории объединенной ветви.

Следующий пример иллюстрирует эту точку.

git init .
touch foo.txt
git add foo.txt
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done
git checkout -B test
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt; git commit -a -m test$i; done
git checkout master
touch foo2.txt
git add foo2.txt
git commit -a -m 4
git merge test
git branch -D test

Теперь история журнала

git log --graph --all

*   commit e905116fed7f4d52c65da46ab6172ae7a08e824a (HEAD -> master)
|\  Merge: 77cc483 584a037
| | Author: 
| | Date:   
| | 
| |     Merge branch 'test'
| | 
| * commit 584a03768822ebd92d4feee20ebe238fffd89c25
| | Author: 
| | Date:   
| | 
| |     test3
| | 
| * commit 7a2ad09b5c39e5076cd22fa957c2d539e37c0861
| | Author: 
| | Date:   
| | 
| |     test2
| | 
| * commit 11ef4fea5ba207a637a85d8e8456f48d0c7bd7ab
| | Author: 
| | Date:   
| | 
| |     test1
| | 
* | commit 77cc4833cf5aac84aca9737945fd79a7632019ac
|/  Author: 
|   Date:   
|   
|       4
| 
* commit 081792ccf9b4714ab4bce23e4e7b126647eeead8
| Author: 
| Date:   
| 
|     3
| 
* commit 971f217200f7e485308b861033b5b31b7ae69d1a
| Author: 
| Date:  
| 
|     2
| 
* commit cef186ae9ad0e316d82c62c2082381747f25a443
  Author: 
  Date:  

      1

Слияние коммитов 2 и 3, как указано выше, создает репозиторий со следующим журналом

git log --graph --all

* commit 3a4010551d50a47a9db6d53a4597770fa2517d92 (HEAD -> master)
| Author: 
| Date:   
| 
|     test3
| 
* commit bbbc747780c847b3d40ed7557ed514e5e4dd9fc2
| Author: 
| Date:  
| 
|     test2
| 
* commit 6f54fd90555fbe6730fcfe7d85761b6477380214
| Author: 
| Date:   
| 
|     test1
| 
* commit 71fa7371198a0cbbf4793dc27ffb27ac65d15096
| Author: 
| Date:  
| 
|     4
| 
* commit 3032069c375ef37b42af75c97759d7a821f1139f
| Author: 
| Date:   
| 
|     s 2 3
| 
* commit cef186ae9ad0e316d82c62c2082381747f25a443
  Author:
  Date:  

      1

Журналы показывают, что я потерял историю ветвления и слияния ветки test. Если после слияния с master ветвь test не была удалена, я получаю результаты, аналогичные первому, с разделенной ветвью test.

Ответы [ 2 ]

2 голосов
/ 13 мая 2019

tl; dr: rebase перебазирует только одну ветвь.

Перебазирование выполняется только в текущей ветви (или в той, которая указана в аргументе). Таким образом, ваши результаты такие же, как и ожидалось, поскольку вы перебазируете только ветку master, а ветвь test остается прежней, то есть ее HEAD указывает на ваши старые коммиты.

Если вы внимательно прочитаете документ git rebase , вы увидите, что он всегда говорит о the current branch.

Если вы хотите, чтобы другие ветви указывали на переназначенные коммиты, вам придется reset их. В этом случае проверка ветки test и использование git reset --hard 194e78fc74d4eaea62a8b903baa6379dfc8578d3 приведут к ожидаемому результату. (Как всегда, будьте осторожны, используя reset --hard, так как он удалит незафиксированные изменения.)

Я не понимаю, какую информацию вы боитесь потерять, поскольку все изменения ветки test присутствуют в перебазированной истории master.

1 голос
/ 13 мая 2019

Понимая, что git rebase - не правильный инструмент, я разработал решение на основе git filter-branch.Идея состоит в том, чтобы изменить коммит "2", добавив содержимое коммита "3" через патч.Затем коммит "3" становится пустым, следовательно, его можно удалить.

Рассматривая второй пример.

git diff 971f217200f7e485308b861033b5b31b7ae69d1a \
         081792ccf9b4714ab4bce23e4e7b126647eeead8 \
         >patch.txt
git filter-branch --tree-filter '\
   if [ "$GIT_COMMIT" == "971f217200f7e485308b861033b5b31b7ae69d1a" ]
   then
       git apply /localdirectory/patch.txt
   fi' \
   --prune-empty -- --all

Поскольку git filter-branch работает во временном подкаталоге .git-rewrite/t, то *Команда 1010 * требует полного пути к файлу исправления.

Это оставляет некоторые старые ссылки.После проверки правильности git log можно выполнить очистку

git update-ref -d refs/original/refs/heads/master

Это даст хранилище с журналом

git log --graph --all

*   commit e905116fed7f4d52c65da46ab6172ae7a08e824a (HEAD -> master)
|\  Merge: 77cc483 584a037
| | Author: 
| | Date:   
| | 
| |     Merge branch 'test'
| | 
| * commit 584a03768822ebd92d4feee20ebe238fffd89c25
| | Author: 
| | Date:   
| | 
| |     test3
| | 
| * commit 7a2ad09b5c39e5076cd22fa957c2d539e37c0861
| | Author: 
| | Date:   
| | 
| |     test2
| | 
| * commit 11ef4fea5ba207a637a85d8e8456f48d0c7bd7ab
| | Author: 
| | Date:   
| | 
| |     test1
| | 
* | commit 77cc4833cf5aac84aca9737945fd79a7632019ac
|/  Author: 
|   Date:   
|   
|       4
| 
* commit 971f217200f7e485308b861033b5b31b7ae69d1a
| Author: 
| Date:  
| 
|     2
| 
* commit cef186ae9ad0e316d82c62c2082381747f25a443
  Author: 
  Date:  

      1

На этом этапе задача по существу выполнена.Тем не менее, может быть полезно изменить сообщение для коммита "2".Это можно сделать с помощью msg-фильтра, например:

git filter-branch --msg-filter 'sed "s/^2/2 and 3 together/"' -- --all
git update-ref -d refs/original/refs/heads/master

В случае, если имеется больше ветвей, как в первом примере, необходимо также удалить соответствующие старые ссылки, например:

git update-ref -d refs/original/refs/heads/test
...