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
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
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.