Merging without changing the working directory

后端 未结 6 1804
北海茫月
北海茫月 2020-12-03 07:36

I have the following scenario:

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

I\'d like to me

6条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-03 08:22

    Alternatively, you can fix the symptoms directly by saving and restoring file timestamps. This is kinda ugly, but it was interesting to write.

    Python Timestamp Save/Restore Script

    #!/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 Test Script

    #!/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
    

    I'll leave it as an exercise for the reader to clean up the Python script to remove extraneous output and add better error checking.

提交回复
热议问题