diff --git a/hgext/fetch.py b/hgext/fetch.py --- a/hgext/fetch.py +++ b/hgext/fetch.py @@ -16,7 +16,7 @@ def fetch(ui, repo, source='default', ** This finds all changes from the repository at the specified path or URL and adds them to the local repository. - If the pulled changes add a new head, the head is automatically + If the pulled changes add a new branch head, the head is automatically merged, and the result of the merge is committed. Otherwise, the working directory is updated to include the new changes. @@ -33,9 +33,10 @@ def fetch(ui, repo, source='default', ** opts['date'] = util.parsedate(date) parent, p2 = repo.dirstate.parents() - if parent != repo.changelog.tip(): - raise util.Abort(_('working dir not at tip ' - '(use "hg update" to check out tip)')) + branch = repo[parent].branch() + if parent != repo[branch].node(): + raise util.Abort(_('working dir not at branch tip ' + '(use "hg update" to check out branch tip)')) if p2 != nullid: raise util.Abort(_('outstanding uncommitted merge')) @@ -50,9 +51,9 @@ def fetch(ui, repo, source='default', ** raise util.Abort(_('outstanding uncommitted changes')) if del_: raise util.Abort(_('working directory is missing some files')) - if len(repo.heads()) > 1: - raise util.Abort(_('multiple heads in this repository ' - '(use "hg heads" and "hg merge" to merge)')) + if len(repo.branchheads(branch)) > 1: + raise util.Abort(_('multiple heads in this branch ' + '(use "hg heads ." and "hg merge" to merge)')) cmdutil.setremoteconfig(ui, opts) @@ -67,27 +68,35 @@ def fetch(ui, repo, source='default', ** else: revs = [other.lookup(rev) for rev in opts['rev']] + # Are there any changes at all? modheads = repo.pull(other, heads=revs) if modheads == 0: return 0 - if modheads == 1: - return hg.clean(repo, repo.changelog.tip()) - newheads = repo.heads(parent) - newchildren = [n for n in repo.heads(parent) if n != parent] + # Is this a simple fast-forward along the current branch? + newheads = repo.branchheads(branch) + newchildren = repo.changelog.nodesbetween([parent], newheads)[2] + if len(newheads) == 1: + if newchildren[0] != parent: + return hg.clean(repo, newchildren[0]) + else: + return + + # Are there more than one additional branch heads? + newchildren = [n for n in newchildren if n != parent] newparent = parent if newchildren: newparent = newchildren[0] hg.clean(repo, newparent) - - newheads = [n for n in repo.heads() if n != newparent] + newheads = [n for n in newheads if n != newparent] if len(newheads) > 1: - ui.status(_('not merging with %d other new heads ' - '(use "hg heads" and "hg merge" to merge them)') % + ui.status(_('not merging with %d other new branch heads ' + '(use "hg heads ." and "hg merge" to merge them)\n') % (len(newheads) - 1)) return + + # Otherwise, let's merge. err = False - if newheads: # By default, we consider the repository we're pulling # *from* as authoritative, so we merge our changes into diff --git a/tests/test-fetch b/tests/test-fetch --- a/tests/test-fetch +++ b/tests/test-fetch @@ -7,6 +7,7 @@ hidehash() { sed "s/changeset 3:........ echo "[extensions]" >> $HGRCPATH echo "fetch=" >> $HGRCPATH +echo % test fetch with default branches only hg init a echo a > a/a hg --cwd a commit -d '1 0' -Ama @@ -66,4 +67,93 @@ rm i/g echo % should abort, because i is modified hg --cwd i fetch ../h + +echo % test fetch with named branches +hg init nbase +echo base > nbase/a +hg -R nbase ci -d '1 0' -Am base +hg -R nbase branch a +echo a > nbase/a +hg -R nbase ci -d '2 0' -m a +hg -R nbase up -C 0 +hg -R nbase branch b +echo b > nbase/b +hg -R nbase ci -Ad '3 0' -m b + +echo +echo % pull in change on foreign branch +hg clone nbase n1 +hg clone nbase n2 +hg -R n1 up -C a +echo aa > n1/a +hg -R n1 ci -d '4 0' -m a1 + +hg -R n2 up -C b +hg -R n2 fetch -d '9 0' -m 'merge' n1 +echo '% parent should be 2 (no automatic update)' +hg -R n2 parents --template '{rev}\n' +rm -fr n1 n2 + +echo +echo % pull in changes on both foreign and local branches +hg clone nbase n1 +hg clone nbase n2 +hg -R n1 up -C a +echo aa > n1/a +hg -R n1 ci -d '4 0' -m a1 +hg -R n1 up -C b +echo bb > n1/b +hg -R n1 ci -d '5 0' -m b1 + +hg -R n2 up -C b +hg -R n2 fetch -d '9 0' -m 'merge' n1 +echo '% parent should be 4 (fast forward)' +hg -R n2 parents --template '{rev}\n' +rm -fr n1 n2 + +echo +echo '% pull changes on foreign (2 new heads) and local (1 new head) branches' +echo % with a local change +hg clone nbase n1 +hg clone nbase n2 +hg -R n1 up -C a +echo a1 > n1/a +hg -R n1 ci -d '4 0' -m a1 +hg -R n1 up -C b +echo bb > n1/b +hg -R n1 ci -d '5 0' -m b1 +hg -R n1 up -C 1 +echo a2 > n1/a +hg -R n1 ci -d '6 0' -m a2 + +hg -R n2 up -C b +echo change >> n2/c +hg -R n2 ci -Ad '7 0' -m local +hg -R n2 fetch -d '9 0' -m 'merge' n1 +echo '% parent should be 7 (new merge changeset)' +hg -R n2 parents --template '{rev}\n' +rm -fr n1 n2 + +echo '% pull in changes on foreign (merge of local branch) and local (2 new' +echo '% heads) with a local change' +hg clone nbase n1 +hg clone nbase n2 +hg -R n1 up -C a +hg -R n1 merge b +hg -R n1 ci -d '4 0' -m merge +hg -R n1 up -C 2 +echo c > n1/a +hg -R n1 ci -d '5 0' -m c +hg -R n1 up -C 2 +echo cc > n1/a +hg -R n1 ci -d '6 0' -m cc + +hg -R n2 up -C b +echo change >> n2/b +hg -R n2 ci -Ad '7 0' -m local +hg -R n2 fetch -d '9 0' -m 'merge' n1 +echo '% parent should be 3 (fetch did not merge anything)' +hg -R n2 parents --template '{rev}\n' +rm -fr n1 n2 + true diff --git a/tests/test-fetch.out b/tests/test-fetch.out --- a/tests/test-fetch.out +++ b/tests/test-fetch.out @@ -1,3 +1,4 @@ +% test fetch with default branches only adding a updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -79,3 +80,93 @@ 1 files updated, 0 files merged, 0 files new changeset 4:55aa4f32ec59 merges remote changes with local % should abort, because i is modified abort: working directory is missing some files +% test fetch with named branches +adding a +marked working directory as branch a +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +marked working directory as branch b +adding b +created new head + +% pull in change on foreign branch +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +pulling from n1 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +% parent should be 2 (no automatic update) +2 + +% pull in changes on both foreign and local branches +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +pulling from n1 +searching for changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 2 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% parent should be 4 (fast forward) +4 + +% pull changes on foreign (2 new heads) and local (1 new head) branches +% with a local change +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +adding c +pulling from n1 +searching for changes +adding changesets +adding manifests +adding file changes +added 3 changesets with 3 changes to 2 files (+2 heads) +updating to 5:708c6cce3d26 +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +merging with 3:d83427717b1f +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +new changeset 7:48f1a33f52af merges remote changes with local +% parent should be 7 (new merge changeset) +7 +% pull in changes on foreign (merge of local branch) and local (2 new +% heads) with a local change +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +pulling from n1 +searching for changes +adding changesets +adding manifests +adding file changes +added 3 changesets with 2 changes to 1 files (+2 heads) +not merging with 1 other new branch heads (use "hg heads ." and "hg merge" to merge them) +% parent should be 3 (fetch did not merge anything) +3