tikhomirov@501: /*
tikhomirov@501:  * Copyright (c) 2012 TMate Software Ltd
tikhomirov@501:  *  
tikhomirov@501:  * This program is free software; you can redistribute it and/or modify
tikhomirov@501:  * it under the terms of the GNU General Public License as published by
tikhomirov@501:  * the Free Software Foundation; version 2 of the License.
tikhomirov@501:  *
tikhomirov@501:  * This program is distributed in the hope that it will be useful,
tikhomirov@501:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
tikhomirov@501:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
tikhomirov@501:  * GNU General Public License for more details.
tikhomirov@501:  *
tikhomirov@501:  * For information on how to redistribute this software under
tikhomirov@501:  * the terms of a license other than GNU General Public License
tikhomirov@501:  * contact TMate Software at support@hg4j.com
tikhomirov@501:  */
tikhomirov@501: package org.tmatesoft.hg.repo.ext;
tikhomirov@501: 
tikhomirov@501: import java.io.File;
tikhomirov@501: import java.util.ArrayList;
tikhomirov@501: import java.util.HashMap;
tikhomirov@501: import java.util.Iterator;
tikhomirov@501: import java.util.Map;
tikhomirov@501: import java.util.NoSuchElementException;
tikhomirov@501: 
tikhomirov@501: import org.tmatesoft.hg.core.HgBadNodeidFormatException;
tikhomirov@501: import org.tmatesoft.hg.core.HgIOException;
tikhomirov@501: import org.tmatesoft.hg.core.Nodeid;
tikhomirov@501: import org.tmatesoft.hg.internal.Internals;
tikhomirov@501: import org.tmatesoft.hg.internal.LineReader;
tikhomirov@501: import org.tmatesoft.hg.repo.HgInvalidFileException;
tikhomirov@501: import org.tmatesoft.hg.repo.HgInvalidStateException;
tikhomirov@501: 
tikhomirov@501: /**
tikhomirov@501:  * Support for standard Rebase extension.
tikhomirov@501:  * 
tikhomirov@501:  * @see http://mercurial.selenic.com/wiki/RebaseExtension
tikhomirov@501:  * @since 1.1
tikhomirov@501:  * @author Artem Tikhomirov
tikhomirov@501:  * @author TMate Software Ltd.
tikhomirov@501:  */
tikhomirov@501: public class Rebase {
tikhomirov@501: 	private Internals repo;
tikhomirov@501: 	private Nodeid workingDirParent;
tikhomirov@501: 	private Nodeid destRevision;
tikhomirov@501: 	private Nodeid externalParent;
tikhomirov@501: 	private Map<Nodeid, Nodeid> state;
tikhomirov@501: 	private boolean collapse;
tikhomirov@501: 	private boolean keepOriginalRevisions;
tikhomirov@501: 	private boolean keepBranchNames;
tikhomirov@501: 	
tikhomirov@501: 	/*package-local*/ Rebase(Internals internalRepo) {
tikhomirov@501: 		repo = internalRepo;
tikhomirov@501: 	}
tikhomirov@501: 
tikhomirov@501: 	public Rebase refresh() throws HgIOException {
tikhomirov@501: 		workingDirParent = null;
tikhomirov@501: 		destRevision = null;
tikhomirov@501: 		externalParent = null;
tikhomirov@501: 		state = null;
tikhomirov@501: 		File f = repo.getFileFromRepoDir("rebasestate");
tikhomirov@501: 		if (!f.exists()) {
tikhomirov@501: 			return this;
tikhomirov@501: 		}
tikhomirov@501: 		state = new HashMap<Nodeid, Nodeid>();
tikhomirov@501: 		try {
tikhomirov@501: 			LineReader lr = new LineReader(f, repo.getSessionContext().getLog());
tikhomirov@501: 			ArrayList<String> contents = new ArrayList<String>();
tikhomirov@501: 			lr.read(new LineReader.SimpleLineCollector(), contents);
tikhomirov@501: 			Iterator<String> it = contents.iterator();
tikhomirov@501: 			workingDirParent = Nodeid.fromAscii(it.next());
tikhomirov@501: 			destRevision = Nodeid.fromAscii(it.next());
tikhomirov@501: 			externalParent = Nodeid.fromAscii(it.next());
tikhomirov@501: 			collapse = "1".equals(it.next());
tikhomirov@501: 			keepOriginalRevisions = "1".equals(it.next());
tikhomirov@501: 			keepBranchNames = "1".equals(it.next());
tikhomirov@501: 			final String nullmerge = "-2";
tikhomirov@501: 			while (it.hasNext()) {
tikhomirov@501: 				String line = it.next();
tikhomirov@501: 				int x = line.indexOf(':');
tikhomirov@501: 				if (x == -1) {
tikhomirov@501: 					throw new HgInvalidStateException(line);
tikhomirov@501: 				}
tikhomirov@501: 				Nodeid oldRev = Nodeid.fromAscii(line.substring(0, x));
tikhomirov@501: 				Nodeid newRev;
tikhomirov@501: 				if (line.regionMatches(x+1, nullmerge, 0, nullmerge.length())) {
tikhomirov@501: 					newRev = null;
tikhomirov@501: 				} else {
tikhomirov@501: 					newRev = Nodeid.fromAscii(line.substring(x+1));
tikhomirov@501: 				}
tikhomirov@501: 				state.put(oldRev, newRev);
tikhomirov@501: 			}
tikhomirov@501: 		} catch (NoSuchElementException ex) {
tikhomirov@501: 			throw new HgIOException("Bad format of rebase state file", f);
tikhomirov@501: 		} catch (HgBadNodeidFormatException ex) {
tikhomirov@501: 			throw new HgIOException("Bad format of rebase state file", ex, f);
tikhomirov@501: 		} catch (HgInvalidFileException ex) {
tikhomirov@501: 			throw new HgIOException("Bad format of rebase state file", ex, f);
tikhomirov@501: 		}
tikhomirov@501: 		return this;
tikhomirov@501: 	}
tikhomirov@501: 	
tikhomirov@501: 	/**
tikhomirov@501: 	 * Tells whether rebase process was interrupted to manually resolve a merge 
tikhomirov@501: 	 * and can be resumed or aborted.
tikhomirov@501: 	 * 
tikhomirov@501: 	 * @return <code>true</code> when rebase is in progress 
tikhomirov@501: 	 */
tikhomirov@501: 	public boolean isRebaseInProgress() {
tikhomirov@501: 		return state != null;
tikhomirov@501: 	}
tikhomirov@501: 
tikhomirov@501: 	public Nodeid getWorkingDirParent() {
tikhomirov@501: 		assert isRebaseInProgress();
tikhomirov@501: 		return workingDirParent;
tikhomirov@501: 	}
tikhomirov@501: 	
tikhomirov@501: 	public Nodeid getTarget() {
tikhomirov@501: 		assert isRebaseInProgress();
tikhomirov@501: 		return destRevision;
tikhomirov@501: 	}
tikhomirov@501: 	
tikhomirov@501: 	public Nodeid getExternalParent() {
tikhomirov@501: 		assert isRebaseInProgress();
tikhomirov@501: 		assert collapse;
tikhomirov@501: 		return externalParent;
tikhomirov@501: 	}
tikhomirov@501: 	
tikhomirov@501: 	public boolean isCollapse() {
tikhomirov@501: 		return collapse;
tikhomirov@501: 	}
tikhomirov@501: 	
tikhomirov@501: 	public boolean isKeepOriginalRevisions() {
tikhomirov@501: 		return keepOriginalRevisions;
tikhomirov@501: 	}
tikhomirov@501: 
tikhomirov@501: 	public boolean isKeepBranchNames() {
tikhomirov@501: 		return keepBranchNames;
tikhomirov@501: 	}
tikhomirov@501: }