代码之家  ›  专栏  ›  技术社区  ›  Idan K

合并而不更改工作目录

git
  •  16
  • Idan K  · 技术社区  · 14 年前

    * ab82147 (HEAD, topic) changes
    * 8993636 changes
    * 82f4426 changes
    * 18be5a3 (master) first
    

    我想合并(非快进) topic 进入之内 master . 这要求我:

    • git checkout master
    • git merge --no-ff topic

    所以我想得到的是:

    *   9075cf4 (HEAD, master) Merge branch 'topic'
    |\  
    | * ab82147 (topic) changes
    | * 8993636 changes
    | * 82f4426 changes
    |/  
    * 18be5a3 first
    

    没有真正接触工作目录(或者至少欺骗git)。

    5 回复  |  直到 14 年前
        1
  •  8
  •   Cascabel    10 年前

    #!/bin/bash
    
    branch=master
    # or take an argument:
    # if [ $@ eq 1 ];
    #      branch="$1";
    # fi
    
    # make sure the branch exists
    if ! git rev-parse --verify --quiet --heads "$branch" > /dev/null; then
         echo "error: branch $branch does not exist"
         exit 1
    fi
    
    # make sure this could be a fast-forward   
    if [ "$(git merge-base HEAD $branch)" == "$(git rev-parse $branch)" ]; then
        # find the branch name associated with HEAD
        currentbranch=$(git symbolic-ref HEAD | sed 's@.*/@@')
        # make the commit
        newcommit=$(echo "Merge branch '$currentbranch'" | git commit-tree $(git log -n 1 --pretty=%T HEAD) -p $branch -p HEAD)
        # move the branch to point to the new commit
        git update-ref -m "merge $currentbranch: Merge made by simulated no-ff" "refs/heads/$branch" $newcommit
    else
        echo "error: merging $currentbranch into $branch would not be a fast-forward"
        exit 1
    fi
    

    有趣的是 newcommit= 线路;它使用提交树直接创建合并提交。第一个参数是要使用的树;那是树的头,你想保留的树枝。提交消息是在stdin上提供的,其余参数命名新提交应该具有的父级。提交的SHA1被打印到stdout,因此假设提交成功,您将捕获它,然后合并该提交(这将是一个快速的过程)。如果你很痴迷,你可以确保提交树成功了-但这应该是几乎可以保证的。

    限制:

    • 这只适用于 可能是
    • reflog消息不同。我是故意这么做的,因为当你使用 --no-ff ,git实际上会强制自己使用默认的(递归)策略,但在reflog中编写这种策略将是一个谎言。
    • 如果你处于超然状态,事情会很糟糕。那就得特别对待了。

    是的,我测试了一个玩具回购,它似乎正常工作(尽管我没有尽力去打破它。)

        2
  •  3
  •   John Bartholomew    14 年前

    我能想到的最简单的方法就是 git clone 对于一个单独的工作副本,在那里进行合并,然后 git pull 回来。拉将是一个快速前进,应该只影响真正已经改变的文件。

    当然,对于这样一个大型项目,制作临时克隆并不理想,而且需要相当大的额外硬盘空间。只要不需要磁盘空间,就可以通过保留合并副本将额外克隆的时间成本降到最低(长期而言)。

    免责声明:我还没有证实这是有效的。但我认为应该这样(git不支持版本文件时间戳)

        3
  •  1
  •   zanerock    5 年前

    绝对有可能进行任何合并,即使是非快进合并,也不需要 git checkout

    cd local_repo
    git worktree add _master_wt master
    cd _master_wt
    git pull origin master:master
    git merge --no-ff -m "merging workbranch" my_work_branch
    cd ..
    git worktree remove _master_wt
    

    master 分支而不切换您的结帐。

        4
  •  0
  •   hlynur    14 年前

    这是一个作弊的版本。

    1. 暂存
    2. git合并--无ff主题
    3. 吉特藏弹
        5
  •  0
  •   John Bartholomew    10 年前

    或者,您可以通过保存和恢复文件时间戳来直接修复症状。这有点难看,但写起来很有趣。

    #!/usr/bin/env python
    
    from optparse import OptionParser
    import os
    import subprocess
    import cPickle as pickle
    
    try:
        check_output = subprocess.check_output
    except AttributeError:
        # check_output was added in Python 2.7, so it's not always available
        def check_output(*args, **kwargs):
            kwargs['stdout'] = subprocess.PIPE
            proc = subprocess.Popen(*args, **kwargs)
            output = proc.stdout.read()
            retcode = proc.wait()
            if retcode != 0:
                cmd = kwargs.get('args')
                if cmd is None:
                    cmd = args[0]
                err = subprocess.CalledProcessError(retcode, cmd)
                err.output = output
                raise err
            else:
                return output
    
    def git_cmd(*args):
        return check_output(['git'] + list(args), stderr=subprocess.STDOUT)
    
    def walk_git_tree(rev):
        """ Generates (sha1,path) pairs for all blobs (files) listed by git ls-tree. """
        tree = git_cmd('ls-tree', '-r', '-z', rev).rstrip('\0')
        for entry in tree.split('\0'):
            print entry
            mode, type, sha1, path = entry.split()
            if type == 'blob':
                yield (sha1, path)
            else:
                print 'WARNING: Tree contains a non-blob.'
    
    def collect_timestamps(rev):
        timestamps = {}
        for sha1, path in walk_git_tree(rev):
            s = os.lstat(path)
            timestamps[path] = (sha1, s.st_mtime, s.st_atime)
            print sha1, s.st_mtime, s.st_atime, path
        return timestamps
    
    def restore_timestamps(timestamps):
        for path, v in timestamps.items():
            if os.path.isfile(path):
                sha1, mtime, atime = v
                new_sha1 = git_cmd('hash-object', '--', path).strip()
                if sha1 == new_sha1:
                    print 'Restoring', path
                    os.utime(path, (atime, mtime))
                else:
                    print path, 'has changed (not restoring)'
            elif os.path.exists(path):
                print 'WARNING: File is no longer a file...'
    
    def main():
        oparse = OptionParser()
        oparse.add_option('--save',
            action='store_const', const='save', dest='action',
            help='Save the timestamps of all git tracked files')
        oparse.add_option('--restore',
            action='store_const', const='restore', dest='action',
            help='Restore the timestamps of git tracked files whose sha1 hashes have not changed')
        oparse.add_option('--db',
            action='store', dest='database',
            help='Specify the path to the data file to restore/save from/to')
    
        opts, args = oparse.parse_args()
        if opts.action is None:
            oparse.error('an action (--save or --restore) must be specified')
    
        if opts.database is None:
            repo = git_cmd('rev-parse', '--git-dir').strip()
            dbpath = os.path.join(repo, 'TIMESTAMPS')
            print 'Using default database:', dbpath
        else:
            dbpath = opts.database
    
        rev = git_cmd('rev-parse', 'HEAD').strip()
        print 'Working against rev', rev
    
        if opts.action == 'save':
            timestamps = collect_timestamps(rev)
            data = (rev, timestamps)
            pickle.dump(data, open(dbpath, 'wb'))
        elif opts.action == 'restore':
            rev, timestamps = pickle.load(open(dbpath, 'rb'))
            restore_timestamps(timestamps)
    
    if __name__ == '__main__':
        main()
    

    Bash测试脚本

    #!/bin/bash
    
    if [ -d working ]; then
        echo "Cowardly refusing to mangle an existing 'working' dir."
        exit 1
    fi
    
    mkdir working
    cd working
    
    # create the repository/working copy
    git init
    
    # add a couple of files
    echo "File added in master:r1." > file-1
    echo "File added in master:r1." > file-2
    mkdir dir
    echo "File added in master:r1." > dir/file-3
    git add file-1 file-2 dir/file-3
    git commit -m "r1: add-1, add-2, add-3"
    git tag r1
    # sleep to ensure new or changed files won't have the same timestamp
    echo "Listing at r1"
    ls --full-time
    sleep 5
    
    # make a change
    echo "File changed in master:r2." > file-2
    echo "File changed in master:r2." > dir/file-3
    echo "File added in master:r2." > file-4
    git add file-2 dir/file-3 file-4
    git commit -m "r2: change-2, change-3, add-4"
    git tag r2
    # sleep to ensure new or changed files won't have the same timestamp
    echo "Listing at r2"
    ls --full-time
    sleep 5
    
    # create a topic branch from r1 and make some changes
    git checkout -b topic r1
    echo "File changed in topic:r3." > file-2
    echo "File changed in topic:r3." > dir/file-3
    echo "File added in topic:r3." > file-5
    git add file-2 dir/file-3 file-5
    git commit -m "r3: change-2, change-3, add-5"
    git tag r3
    # sleep to ensure new or changed files won't have the same timestamp
    echo "Listing at r3"
    ls --full-time
    sleep 5
    
    echo "Saving timestamps"
    ../save-timestamps.py --save
    
    echo "Checking out master and merging"
    # merge branch 'topic'
    git checkout master
    git merge topic
    echo "File changed in topic:r3." > file-2 # restore file-2
    echo "File merged in master:r4." > dir/file-3
    git add file-2 dir/file-3
    git commit -m "r4: Merge branch 'topic'"
    git tag r4
    echo "Listing at r4"
    ls --full-time
    
    echo "Restoring timestamps"
    ../save-timestamps.py --restore
    ls --full-time
    

    我将把它作为一个练习留给读者来清理Python脚本,以删除无关的输出并添加更好的错误检查。

        6
  •  0
  •   CodesInTheDark    6 年前
    1. 将主机合并到主题:git将源/主机合并或将主题合并到自身
    2. 更改主控形状:git update ref/heads/master refs/heads/topic

    现在您可以回到主题中的pre-merge commit:git reset HEAD~