Ejemplo n.º 1
0
def edit_branch_description(branchname, description=None):
    description = description or raw_input('enter description:')
    config = _get_repo().repo.get_config()
    if not branchname in _get_repo().branches:
        GitError('{} is not an existing branch'.format(branchname))
        config.set(('branch',branchname),'description',description)
        config.write_to_path()
Ejemplo n.º 2
0
def create_branch(new_branch, base_rev, force=False ,no_track=False  ):
        """Try creating a new branch which tracks the given remote
            if such a branch does not exist then branch off a local branch
        """
        repo=_get_repo()
        
        # Already exists
        if new_branch in repo.branches:
            if not force:
                raise GitError("branch %s already exists\n use --force to overwrite anyway" % new_branch)
       
         # fork with new sha
        new_ref = repo._format_ref_branch(new_branch)
        base_sha=find_revision_sha(repo,base_rev)
        repo.repo.refs[new_ref] = base_sha
        
        #handle tracking, only if this was a remote
        tracking,remote_branch =( ['origin']+base_rev.split('/'))[-2:]  #branch-> origin/branch.  remote/branch stays as is
        qualified_remote_branch=os.path.sep.join([tracking,remote_branch])
        if qualified_remote_branch in repo.remote_branches and not base_rev in repo.branches:
            if not no_track:
                add_tracking(new_branch,tracking,remote_branch)
            else:
                remove_tracking(new_branch)

        #todo reflog
        return new_ref
Ejemplo n.º 3
0
def delete_branch(delete_branchname,force=False,remote=None, verbose=0):
    '''delete a branch.  
    if remote=True, then look in refs/remotes, otherwise check refs/heads
    for local, check if it has a remote tracking branch, and only allow delete if upstream has merged
    '''
    print 'delete',delete_branchname,force,remote
    repo=_get_repo()
    if remote:
        qualified_branch=repo._format_ref_remote(delete_branchname)
    else:
        qualified_branch=repo._format_ref_branch(delete_branchname)
        if delete_branchname == repo.active_branch:
            GitError('Cannot delete active branch.  ')


    remote_tracking_branch=get_remote_tracking_branch(repo,delete_branchname)

    if remote_tracking_branch and not force:
        #see if local is ahead of remote
        commits_ahead=count_commits_between(repo,
                                 repo.refs[qualified_branch],
                                 repo.remote_branches[remote_tracking_branch] 
                                 )[0]
        if commits_ahead:
            raise GitError('{0} is ahead of {1} by {2} commits.\nuse git branch -D\n'.format(delete_branchname,
                                    remote_tracking_branch,
                                    commits_ahead))
    print 'removing {} (was {})\n'.format(delete_branchname,repo.refs[qualified_branch])
    del repo.repo.refs[qualified_branch]

    if not remote:
        remove_tracking(delete_branchname)
Ejemplo n.º 4
0
def delete_branch(delete_branchname, force=False, remote=None, verbose=0):
    '''delete a branch.  
    if remote=True, then look in refs/remotes, otherwise check refs/heads
    for local, check if it has a remote tracking branch, and only allow delete if upstream has merged
    '''
    print 'delete', delete_branchname, force, remote
    repo = _get_repo()
    if remote:
        qualified_branch = repo._format_ref_remote(delete_branchname)
    else:
        qualified_branch = repo._format_ref_branch(delete_branchname)
        if delete_branchname == repo.active_branch:
            GitError('Cannot delete active branch.  ')

    remote_tracking_branch = get_remote_tracking_branch(
        repo, delete_branchname)

    if remote_tracking_branch and not force:
        #see if local is ahead of remote
        commits_ahead = count_commits_between(
            repo, repo.refs[qualified_branch],
            repo.remote_branches[remote_tracking_branch])[0]
        if commits_ahead:
            raise GitError(
                '{0} is ahead of {1} by {2} commits.\nuse git branch -D\n'.
                format(delete_branchname, remote_tracking_branch,
                       commits_ahead))
    print 'removing {} (was {})\n'.format(delete_branchname,
                                          repo.refs[qualified_branch])
    del repo.repo.refs[qualified_branch]

    if not remote:
        remove_tracking(delete_branchname)
Ejemplo n.º 5
0
def branch_list(result):
    # TODO: tracking branches
    N = result.abbrev
    repo = _get_repo()
    if not result.remotes:
        for key, value in repo.branches.iteritems():
            dispval = value[0:N]  #todo, --abbrev=n
            commitmsg = (repo[value].message if result.verbose else '').strip()
            tracking = get_remote_tracking_branch(repo, key)
            trackmsg = ''
            diffmsg = trackingsha = ''
            if tracking:
                trackingsha = repo.remote_branches[tracking]
                ahead, behind = count_commits_between(repo, value, trackingsha)
                diffmsg = '+{}/-{} compare to'.format(
                    ahead, behind) if result.verbose else ''
                trackmsg = '[{} {} {}]'.format(diffmsg, tracking,
                                               trackingsha[0:N])
            print('* ' if repo.active_branch == key else
                  '') + key, dispval, trackmsg, commitmsg
    if result.remotes or result.all:
        for key, value in repo.remote_branches.iteritems():
            dispval = value[0:N]  #todo, --abbrev=n
            commitmsg = (repo[value].message if result.verbose else '').strip()
            print('* ' if repo.active_branch == key else
                  '') + key, dispval, commitmsg
Ejemplo n.º 6
0
def create_branch(new_branch, base_rev, force=False, no_track=False):
    """Try creating a new branch which tracks the given remote
            if such a branch does not exist then branch off a local branch
        """
    repo = _get_repo()

    # Already exists
    if new_branch in repo.branches:
        if not force:
            raise GitError(
                "branch %s already exists\n use --force to overwrite anyway" %
                new_branch)

    # fork with new sha
    new_ref = repo._format_ref_branch(new_branch)
    base_sha = find_revision_sha(repo, base_rev)
    repo.repo.refs[new_ref] = base_sha

    #handle tracking, only if this was a remote
    tracking, remote_branch = (['origin'] + base_rev.split('/'))[
        -2:]  #branch-> origin/branch.  remote/branch stays as is
    qualified_remote_branch = os.path.sep.join([tracking, remote_branch])
    if qualified_remote_branch in repo.remote_branches and not base_rev in repo.branches:
        if not no_track:
            add_tracking(new_branch, tracking, remote_branch)
        else:
            remove_tracking(new_branch)

    #todo reflog
    return new_ref
Ejemplo n.º 7
0
def add_tracking(branchname, remote, remotebranch):
        # Get repo's config
        config = _get_repo().repo.get_config()

        # Add new entries for remote
        config.set(('branch', branchname), 'remote', remote)
        config.set(('branch', branchname), 'merge', 'refs/heads/'+remotebranch)

        # Write to disk
        config.write_to_path()
Ejemplo n.º 8
0
def add_tracking(branchname, remote, remotebranch):
    # Get repo's config
    config = _get_repo().repo.get_config()

    # Add new entries for remote
    config.set(('branch', branchname), 'remote', remote)
    config.set(('branch', branchname), 'merge', 'refs/heads/' + remotebranch)

    # Write to disk
    config.write_to_path()
Ejemplo n.º 9
0
def remove_tracking(branchname):
    '''remove branch entry from config'''
    # Get repo's config
    config = _get_repo().repo.get_config()
    try:
        del config[('branch', branchname)]['remote']
        del config[('branch', branchname)]['merge']
        if not config[('branch', branchname)]:
            del config[('branch', branchname)]
    except KeyError:
        pass
    
    # Write to disk
    config.write_to_path()
Ejemplo n.º 10
0
def remove_tracking(branchname):
    '''remove branch entry from config'''
    # Get repo's config
    config = _get_repo().repo.get_config()
    try:
        del config[('branch', branchname)]['remote']
        del config[('branch', branchname)]['merge']
        if not config[('branch', branchname)]:
            del config[('branch', branchname)]
    except KeyError:
        pass

    # Write to disk
    config.write_to_path()
Ejemplo n.º 11
0
def move_branch(movebranch,force,verbose):
    '''move oldbranch (or active_branch) to newbranch. update config if needed'''
    repo=_get_repo()
    oldbranch,newbranch=([repo.active_branch]+movebranch)[-2:]

    if oldbranch not in repo.branches:
        raise GitError('{} does not exist in branches'.format(oldbranch))
    if newbranch in repo.branches and not force:
        raise GitError('{} already exists.  use -M to force overwriting'.format(newbranch))
    if newbranch != oldbranch:
        print 'Renaming {} ({}) to {}\n'.format(oldbranch,repo.branches[oldbranch],newbranch)
        repo.add_ref(repo._format_ref_branch(newbranch),repo._format_ref_branch(oldbranch))
        del repo.repo.refs[repo._format_ref_branch(oldbranch)]
        #todo: reflog
    if oldbranch == repo.active_branch:
        repo.active_branch=newbranch
Ejemplo n.º 12
0
def move_branch(movebranch,force,verbose):
    '''move oldbranch (or active_branch) to newbranch. update config if needed'''
    repo=_get_repo()
    oldbranch,newbranch=([repo.active_branch]+movebranch)[-2:]

    if oldbranch not in repo.branches:
        raise GitError('{} does not exist in branches'.format(oldbranch))
    if newbranch in repo.branches and not force:
        raise GitError('{} already exists.  use -M to force overwriting'.format(newbranch))
    if newbranch != oldbranch:
        print 'Renaming {} ({}) to {}\n'.format(oldbranch,repo.branches[oldbranch],newbranch)
        repo.add_ref(repo._format_ref_branch(newbranch),repo._format_ref_branch(oldbranch))
        del repo.repo.refs[repo._format_ref_branch(oldbranch)]
        #todo: reflog
    if oldbranch == repo.active_branch:
        repo.active_branch=newbranch
Ejemplo n.º 13
0
def branch_list(result):
        # TODO: tracking branches
        N=result.abbrev
        repo = _get_repo()
        if not result.remotes:
            for key,value in repo.branches.iteritems():
                dispval=value[0:N]  #todo, --abbrev=n
                commitmsg=(repo[value].message if result.verbose else '').strip()
                tracking=get_remote_tracking_branch(repo,key)
                trackmsg=''
                diffmsg=trackingsha=''
                if tracking:
                    trackingsha=repo.remote_branches[tracking]
                    ahead,behind= count_commits_between(repo,value,trackingsha)
                    diffmsg='+{}/-{} compare to'.format(ahead,behind) if result.verbose else ''
                    trackmsg='[{} {} {}]'.format(diffmsg,tracking,trackingsha[0:N])
                print ('* ' if repo.active_branch == key else '') + key, dispval, trackmsg, commitmsg
        if result.remotes or result.all:
            for key, value in repo.remote_branches.iteritems():
                dispval=value[0:N]  #todo, --abbrev=n
                commitmsg=(repo[value].message if result.verbose else '').strip()
                print ('* ' if repo.active_branch == key else '') + key,  dispval, commitmsg
Ejemplo n.º 14
0
def merge(args):
    helptext='''git merge' [--msg <msg>] [<commit>]
    git merge --abort\n
    
    merges <commit> into HEAD, or remote tracking branch if commit not specified.
    <commit> can be a local or remote ref, or an existing commit sha.

    merge will handle unambiguous conflicts between head and other 
    merge head, and will insert conflict markers if conflicts cannot be resolved.  
    note that the strategy used will prefer changes in the local head.  
    for instance, if HEAD deleted a section, while MERGE_HEAD modified the same 
    action, the section will be deleted from the final without indicating a conflict.
      
    be sure to commit any local changes before running merge, as files in working tree (i.e on disk) are changed, and checked in, which will probably overwrite any local uncomitted changes.
    
    note merge will not actually commit anything.  run git commit to commit a successful merge.

    
    --abort will remove the MERGE_HEAD and MERGE_MSG files, and will reset staging area, but wont affect files on disk.  use git reset --hard or git checkout if this is desired.
    '''
    repo=_get_repo()
    print '_'*30

    parser=argparse.ArgumentParser(prog='merge', usage=helptext)
    parser.add_argument('commit',action='store',nargs='?', help='commit sha, local branch, or remote branch name to merge from')
    parser.add_argument('--msg',nargs=1,action='store',help='commit message to store')
    parser.add_argument('--abort',action='store_true',help='abort in progress merge attempt')
    result=parser.parse_args(args)
    
    if result.abort:
        print 'attempting to undo merge.  beware, files in working tree are not touched.  \nused git reset --hard to revert particular files'
        git_reset([])
        os.remove(os.path.join(repo.repo.controldir(),'MERGE_HEAD'))
        os.remove(os.path.join(repo.repo.controldir(),'MERGE_MSG'))

    #todo: check for uncommitted changes and confirm
    
    # first, determine merge head
    merge_head = find_revision_sha(repo,result.commit or get_remote_tracking_branch(repo,repo.active_branch))
    if not merge_head:
        raise GitError('must specify a commit sha, branch, remote tracking branch to merge from.  or, need to set-upstream branch using git branch --set-upstream <remote>[/<branch>]')

    head=find_revision_sha(repo,repo.active_branch)

    base_sha=merge_base(repo,head,merge_head)[0]  #fixme, what if multiple bases

    if base_sha==head:
        print 'Fast forwarding {} to {}'.format(repo.active_branch,merge_head)
        repo.refs['HEAD']=merge_head
        return 
    if base_sha == merge_head:
        print 'head is already up to date'
        return  
    
    print 'merging <{}> into <{}>\n{} commits ahead of merge base <{}> respectively'.format(merge_head[0:7],head[0:7],count_commits_between(repo,merge_head,head),base_sha[0:7])
    base_tree=repo[base_sha].tree
    merge_head_tree=repo[merge_head].tree
    head_tree=repo[head].tree

    num_conflicts,added,removed=merge_trees(repo.repo.object_store, base_tree,head_tree,merge_head_tree)
    # update index
    if added: 
        porcelain.add(repo.path, added)
    if removed: 
        porcelain.rm(repo.path, removed)

    repo.repo._put_named_file('MERGE_HEAD',merge_head)
    repo.repo._put_named_file('MERGE_MSG','Merged from {}({})'.format(merge_head, result.commit))
    print 'Merge complete with {} conflicted files'.format(num_conflicts)
    print '''Merged files were added to the staging area, but have not yet been comitted.   
Ejemplo n.º 15
0
def branch(args):
    repo=_get_repo()
    
    parser = argparse.ArgumentParser(prog='git branch'
                                     , description="List, create, or delete branches")
    #list
    list_grp=parser.add_mutually_exclusive_group(required= False)
    list_grp.add_argument('-r','--remotes',action='store_true',help='list or delete remotep tracking branches')
    list_grp.add_argument('-a','--all',action='store_true',help='list both remote and local branches') 
    
    # move type commands
    move_type=parser.add_mutually_exclusive_group(required=False)
    move_type.add_argument('-m','--move', nargs='+', metavar=('[oldbranch]','newbranch'), help='move/rename oldbranch or HEAD')
    move_type.add_argument('-M',nargs='+',metavar=('[oldbranch]','newbranch'),help='move/rename even if branch already exists')

    # delete type commands
    delete_flags=parser.add_mutually_exclusive_group(required=False)
    delete_flags.add_argument('-d','--delete', nargs=1, metavar=('branchname'), help='delete branchname,TODO: branch must be fully merged with upstream ')
    delete_flags.add_argument('-D',nargs=1,metavar=('branchname'),help='Delete a branch irrespective of its merged status.')

    # misc flags
    parser.add_argument('-v','--verbose',action='count', help='When in list mode, show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print the name of the upstream branch, as well (see also git remote show <remote>).')
    parser.add_argument('-f','--force',action='store_true', help='Reset <branchname> to <startpoint> if <branchname> exists already. Without -f git branch refuses to change an existing branch.')
    abbrevgrp=parser.add_mutually_exclusive_group()
    abbrevgrp.add_argument('--abbrev',action='store',nargs='?',help='set number of characters to display in sha',type=int,default=7)
    abbrevgrp.add_argument('--no-abbrev',action='store_const',help='do not abbreviate sha ',const=40,dest='abbrev')
    track_flags=parser.add_mutually_exclusive_group(required=False )

    track_flags.add_argument('--set-upstream',action='store', nargs=2, metavar=('branchname','upstream') ,help='set branchname to track upstream')
    track_flags.add_argument('--no-track', nargs='+',metavar=('branchname','startpoint'),help='set existing branch to not track, or create new branch that doesnt track')
    
    
    # add_branch 
    parser.add_argument('branchname',nargs='?')
    parser.add_argument('startpoint',nargs='?')

    
    parser.add_argument('--edit_description',action='store',nargs='?',metavar='branchname', const=repo.active_branch)
    
    
    result = parser.parse_args(args)

    # combine args
    edit_description=result.edit_description
    delete_branchname=result.delete or result.D
    move_branchname = result.move or result.M
    no_track=result.no_track
    add_branchname = (result.branchname, result.startpoint or repo.active_branch)
    set_upstream= result.set_upstream

    force = result.force or result.D or result.M
    mutual_exclusive_list=( delete_branchname, 
                           move_branchname, 
                           edit_description, 
                           result.branchname, 
                           set_upstream,
                           no_track)
    list_flag=not any_one(mutual_exclusive_list)
    
    if not any_one((list_flag,)+ mutual_exclusive_list):
        raise GitError('too many options specified.\n'+parser.print_help())
        
    
    if list_flag:
        branch_list(result) 
    elif delete_branchname:
        delete_branch(delete_branchname[0], force , result.remotes, result.verbose)
    elif move_branchname:
        move_branch(move_branchname, force, result.verbose)
    elif add_branchname[0]:
        create_branch(add_branchname[0],add_branchname[1],force,False )
    elif edit_description:
        edit_branch_description(edit_description)
    elif set_upstream:
        add_tracking(set_upstream[0], *( ['origin']+set_upstream[1].split('/'))[-2:])
        print set_upstream[0], format_tracking_branch_desc(repo,set_upstream[0])
    elif no_track:
        if len(no_track)==1:
            remove_tracking(no_track[0])
        else:
            create_branch(no_track[0],no_track[1],force,True)
Ejemplo n.º 16
0
def branch(args):
    repo = _get_repo()

    parser = argparse.ArgumentParser(
        prog='git branch', description="List, create, or delete branches")
    #list
    list_grp = parser.add_mutually_exclusive_group(required=False)
    list_grp.add_argument('-r',
                          '--remotes',
                          action='store_true',
                          help='list or delete remotep tracking branches')
    list_grp.add_argument('-a',
                          '--all',
                          action='store_true',
                          help='list both remote and local branches')

    # move type commands
    move_type = parser.add_mutually_exclusive_group(required=False)
    move_type.add_argument('-m',
                           '--move',
                           nargs='+',
                           metavar=('[oldbranch]', 'newbranch'),
                           help='move/rename oldbranch or HEAD')
    move_type.add_argument('-M',
                           nargs='+',
                           metavar=('[oldbranch]', 'newbranch'),
                           help='move/rename even if branch already exists')

    # delete type commands
    delete_flags = parser.add_mutually_exclusive_group(required=False)
    delete_flags.add_argument(
        '-d',
        '--delete',
        nargs=1,
        metavar=('branchname'),
        help=
        'delete branchname,TODO: branch must be fully merged with upstream ')
    delete_flags.add_argument(
        '-D',
        nargs=1,
        metavar=('branchname'),
        help='Delete a branch irrespective of its merged status.')

    # misc flags
    parser.add_argument(
        '-v',
        '--verbose',
        action='count',
        help=
        'When in list mode, show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print the name of the upstream branch, as well (see also git remote show <remote>).'
    )
    parser.add_argument(
        '-f',
        '--force',
        action='store_true',
        help=
        'Reset <branchname> to <startpoint> if <branchname> exists already. Without -f git branch refuses to change an existing branch.'
    )
    abbrevgrp = parser.add_mutually_exclusive_group()
    abbrevgrp.add_argument('--abbrev',
                           action='store',
                           nargs='?',
                           help='set number of characters to display in sha',
                           type=int,
                           default=7)
    abbrevgrp.add_argument('--no-abbrev',
                           action='store_const',
                           help='do not abbreviate sha ',
                           const=40,
                           dest='abbrev')
    track_flags = parser.add_mutually_exclusive_group(required=False)

    track_flags.add_argument('--set-upstream',
                             action='store',
                             nargs=2,
                             metavar=('branchname', 'upstream'),
                             help='set branchname to track upstream')
    track_flags.add_argument(
        '--no-track',
        nargs='+',
        metavar=('branchname', 'startpoint'),
        help=
        'set existing branch to not track, or create new branch that doesnt track'
    )

    # add_branch
    parser.add_argument('branchname', nargs='?')
    parser.add_argument('startpoint', nargs='?')

    parser.add_argument('--edit_description',
                        action='store',
                        nargs='?',
                        metavar='branchname',
                        const=repo.active_branch)

    result = parser.parse_args(args)

    # combine args
    edit_description = result.edit_description
    delete_branchname = result.delete or result.D
    move_branchname = result.move or result.M
    no_track = result.no_track
    add_branchname = (result.branchname, result.startpoint
                      or repo.active_branch)
    set_upstream = result.set_upstream

    force = result.force or result.D or result.M
    mutual_exclusive_list = (delete_branchname, move_branchname,
                             edit_description, result.branchname, set_upstream,
                             no_track)
    list_flag = not any_one(mutual_exclusive_list)

    if not any_one((list_flag, ) + mutual_exclusive_list):
        raise GitError('too many options specified.\n' + parser.print_help())

    if list_flag:
        branch_list(result)
    elif delete_branchname:
        delete_branch(delete_branchname[0], force, result.remotes,
                      result.verbose)
    elif move_branchname:
        move_branch(move_branchname, force, result.verbose)
    elif add_branchname[0]:
        create_branch(add_branchname[0], add_branchname[1], force, False)
    elif edit_description:
        edit_branch_description(edit_description)
    elif set_upstream:
        add_tracking(set_upstream[0],
                     *(['origin'] + set_upstream[1].split('/'))[-2:])
        print set_upstream[0], format_tracking_branch_desc(
            repo, set_upstream[0])
    elif no_track:
        if len(no_track) == 1:
            remove_tracking(no_track[0])
        else:
            create_branch(no_track[0], no_track[1], force, True)