代码之家  ›  专栏  ›  技术社区  ›  Ben Hocking

为什么git-rebase会给我合并冲突,而我所做的只是压缩提交?

  •  107
  • Ben Hocking  · 技术社区  · 15 年前

    下面是我正在使用的脚本的修改版本:


    repo\u squash.sh(这是实际运行的脚本):


    rm -rf repo_squash
    git clone repo repo_squash
    cd repo_squash/
    GIT_EDITOR=../repo_squash_helper.sh git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
    

    repo\u squash\u helper.sh(此脚本仅由repo\u squash.sh使用):


    if grep -q "pick " $1
    then
    #  cp $1 ../repo_squash_history.txt
    #  emacs -nw $1
      sed -f ../repo_squash_list.txt < $1 > $1.tmp
      mv $1.tmp $1
    else
      if grep -q "initial import" $1
      then
        cp ../repo_squash_new_message1.txt $1
      elif grep -q "fixing bad import" $1
      then
        cp ../repo_squash_new_message2.txt $1
      else
        emacs -nw $1
      fi
    fi
    

    repo\u squash\u list.txt:(此文件仅由repo\u squash\u helper.sh使用)


    # Initial import
    s/pick \(251a190\)/squash \1/g
    # Leaving "Needed subdir" for now
    # Fixing bad import
    s/pick \(46c41d1\)/squash \1/g
    s/pick \(5d7agf2\)/squash \1/g
    s/pick \(3da63ed\)/squash \1/g
    

    我将把“新消息”的内容留给你想象。最初,我没有使用“-strategy theirs”选项(也就是说,使用默认策略,如果我正确理解文档,它是递归的,但是我不确定使用哪个递归策略),它也不起作用。另外,我应该指出,使用repo\u squash\u helper.sh中注释掉的代码,我保存了sed脚本工作的原始文件,并对其运行sed脚本,以确保它正在执行我希望它执行的操作(它是)。再说一次,我都不知道为什么会这样

    在处理我们庞大的“真实”存储库之前,我在一个测试存储库中使用了类似的脚本。这是一个非常简单的存储库,测试运行得非常干净。

    Finished one cherry-pick.
    # Not currently on any branch.
    nothing to commit (working directory clean)
    Could not apply 66c45e2... Needed subdir
    

    这是第一次压缩提交后的第一个选择。跑步 git status git rebase --continue ,我得到了一个非常类似的消息后,几次提交。如果我再这样做一次,我得到另一个非常类似的消息后,几十个提交。如果我再这样做,这一次它将经历大约100次提交,并产生以下消息:

    Automatic cherry-pick failed.  After resolving the conflicts,
    mark the corrected paths with 'git add <paths>', and
    run 'git rebase --continue'
    Could not apply f1de3bc... Incremental
    

    如果我再跑 git状态 ,我得到:

    # Not currently on any branch.
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    # modified:   repo/file_A.cpp
    # modified:   repo/file_B.cpp
    #
    # Unmerged paths:
    #   (use "git reset HEAD <file>..." to unstage)
    #   (use "git add/rm <file>..." as appropriate to mark resolution)
    #
    # both modified:      repo/file_X.cpp
    #
    # Changed but not updated:
    #   (use "git add/rm <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    # deleted:    repo/file_Z.imp
    

    “两个修改”听起来有点奇怪,因为这只是一个选择的结果。另外值得注意的是,如果我看一下“冲突”,它可以归结为一行,一个版本以[tab]字符开头,另一个版本有四个空格。这听起来像是一个问题,我如何设置我的配置文件,但没有在它的排序(我确实注意到core.ignorecase设置为true,但显然git clone是自动设置的。考虑到原始源代码是在Windows机器上的,我对此并不完全感到惊讶。)

    如果我手动修复文件\u X.cpp,它不久就会失败,并出现另一个冲突,这次是一个版本认为应该存在的文件(CMakeLists.txt)和一个版本认为不应该存在的文件(CMakeLists.txt)之间的冲突。如果我通过说我确实需要这个文件(我确实需要),来修复这个冲突,那么几次提交之后,我会得到另一个冲突(在同一个文件中),其中现在有一些非常重要的更改。在这场冲突中只有25%是这样。

    更新#2:

    rm -rf repo_squash
    git clone repo repo_squash
    cd repo_squash/
    git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
    

    然后,我就照原样接受了原稿。也就是说,“再基础”不应该改变什么。结果与前面描述的结果相同。

    更新#3:

    或者,如果我省略策略并将最后一个命令替换为:

    git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a
    

    我不再会遇到“无需承诺”的重新设置基础问题,但我仍然会遇到其他冲突。

    #========================================================
    # Initialize directories
    #========================================================
    rm -rf test_squash/ test_squash_clone/
    mkdir -p test_squash
    mkdir -p test_squash_clone
    #========================================================
    
    #========================================================
    # Create repository with history
    #========================================================
    cd test_squash/
    git init
    echo "README">README
    git add README
    git commit -m"Initial commit: can't easily access for rebasing"
    echo "Line 1">test_file.txt
    git add test_file.txt
    git commit -m"Created single line file"
    echo "Line 2">>test_file.txt 
    git add test_file.txt 
    git commit -m"Meant for it to be two lines"
    git checkout -b dev
    echo Meaningful code>new_file.txt
    git add new_file.txt 
    git commit -m"Meaningful commit"
    git checkout master
    echo Conflicting meaningful code>new_file.txt
    git add new_file.txt 
    git commit -m"Conflicting meaningful commit"
    # This will conflict
    git merge dev
    # Fixes conflict
    echo Merged meaningful code>new_file.txt
    git add new_file.txt
    git commit -m"Merged dev with master"
    cd ..
    
    #========================================================
    # Save off a clone of the repository prior to squashing
    #========================================================
    git clone test_squash test_squash_clone
    #========================================================
    
    #========================================================
    # Do the squash
    #========================================================
    cd test_squash
    GIT_EDITOR=../test_squash_helper.sh git rebase -i HEAD@{7}
    #========================================================
    
    #========================================================
    # Show the results
    #========================================================
    git log
    git gc
    git reflog
    #========================================================
    

    test\u squash\u helper.sh(由test\u sqash.sh使用):

    # If the file has the phrase "pick " in it, assume it's the log file
    if grep -q "pick " $1
    then
      sed -e "s/pick \(.*\) \(Meant for it to be two lines\)/squash \1 \2/g" < $1 > $1.tmp
      mv $1.tmp $1
    # Else, assume it's the commit message file
    else
    # Use our pre-canned message
      echo "Created two line file" > $1
    fi
    

    附言:是的,我知道你们中的一些人看到我使用emacs作为后备编辑器时会畏缩。

    附言:我们知道,在重新设置基础之后,我们必须将现有存储库的所有克隆都吹走(按照“发布存储库后,不应重新设置存储库的基础”这句话)

    有人能告诉我怎么在这上面加个赏金吗?无论是在编辑模式还是查看模式,我都看不到这个屏幕上的任何地方。

    5 回复  |  直到 15 年前
        1
  •  77
  •   Cascabel    15 年前

    好吧,我有足够的信心给出答案。也许得修改一下,但我知道你的问题所在。

    你的玩具回购测试用例有一个合并-更糟的是,它有一个冲突合并。你在合并中重新调整位置。没有 -p (这并不完全适用于 -i 当rebase试图挑选下一个提交时,它的补丁可能不适用(我认为这是一个合并冲突,因为 git cherry-pick

    不幸的是,正如我们在评论中指出的, -第 相信 rebase -i 之前 合并。)

    假设我们有一个非常简单的例子,我们想把a,B,和C挤在一起:

    - o - A - B - C - X - D - E - F (master)
       \             /
        Z -----------
    

    git rebase -i -p 如你所料。

    如果有冲突,事情会变得更棘手。它可以进行很好的挤压,但是当它尝试重新创建合并时,冲突会再次发生。您必须再次解析它们,将它们添加到索引中,然后使用 git rebase --continue 继续前进(当然,您可以通过从原始合并提交中签出版本来再次解析它们。)

    rerere rerere.enabled 设置为true),这将更容易-git将能够 使用 重新 绳状 从最初出现冲突时开始,您所要做的就是检查它以确保它正常工作,将文件添加到索引中,然后继续(你甚至可以更进一步,打开 rerere.autoupdate ,它将为您添加它们,这样合并甚至不会失败)。不过,我猜你没有启用rere,所以你必须自己解决冲突*

    rerere-train.sh 来自git contrib的脚本,它试图“从现有的合并提交中初始化数据库”——基本上,它检查所有的合并提交,尝试合并它们,如果合并失败,它会获取结果并将它们显示给用户 git-rerere

        2
  •  109
  •   hlidka    5 年前

    如果您不介意创建一个新分支,我就是这样处理这个问题的:

    # create a new branch
    git checkout -b new_clean_branch
    
    # apply all changes
    git merge original_messy_branch
    
    # forget the commits but have the changes staged for commit
    git reset --soft main        
    
    git commit -m "Squashed changes from original_messy_branch"
    
        3
  •  6
  •   M. Porooshani    9 年前

    我正在寻找一个类似的要求,即放弃我的开发分支的中间提交,我发现这个过程对我很有用。
    在我的工作部门

    git reset –hard mybranch-start-commit
    git checkout mybranch-end-commit . // files only of the latest commit
    git add -a
    git commit -m”New Message intermediate commits discarded”
    

    我们已经将最新的提交连接到分支的开始提交!

        4
  •  2
  •   Felix Dombek    4 年前

    如果要从提交的长分支(其中一些是合并提交)中创建一个提交,最简单的方法是将分支重置到第一次提交之前的点,同时保留所有更改,然后重新提交它们:

    git reset $(git merge-base origin/master @)
    git add .
    git commit
    

    替换 origin/master 你的分支机构名称。

    add . 是必需的,因为新添加的文件在重置后显示为未跟踪。

        5
  •  1
  •   JonoB    5 年前

    建立在@ hlidka 上面的一个很好的答案是尽量减少手动干预,我想添加一个版本来保留主服务器上不在要挤压的分支中的任何新提交。

    因为我相信这些很容易在 git reset 举个例子。

    # create a new branch 
    # ...from the commit in master original_messy_branch was originally based on. eg 5654da06
    git checkout -b new_clean_branch 5654da06
    
    # apply all changes
    git merge original_messy_branch
    
    # forget the commits but have the changes staged for commit
    # ...base the reset on the base commit from Master
    git reset --soft 5654da06       
    
    git commit -m "Squashed changes from original_messy_branch"
    
    # Rebase onto HEAD of master
    git rebase origin/master
    
    # Resolve any new conflicts from the new commits
    
        6
  •  0
  •   VonC    12 年前

    请注意 -X 在交互式重新基准中使用时,策略选项被忽略。

    commit db2b3b820e2b28da268cc88adff076b396392dfe (2013年7月,git 1.8.4+),

    合并策略及其选项可以在中指定 git rebase -- interactive ,他们被完全忽视了。

    签字人:Arnaud Fontaine

    这意味着 -十 策略现在可以与交互式重新基以及普通重新基一起工作,并且您的初始脚本现在可以更好地工作。

        7
  •  0
  •   abaldwinhunter    9 年前

    我遇到了一个更简单但类似的问题,我有 1) 解决了本地分支上的合并冲突, 2) 一直在努力增加更多的小承诺, 3) 希望重新设置基础并解决合并冲突。

    为了我, git rebase -p -i master 工作。它保持了最初的冲突解决承诺,让我压扁了其他人。

    希望能帮助到别人!