changeset 106:729438d70af8

* Retrieve and store current branch and heads; some refactoring
author Chris Cannam
date Thu, 25 Nov 2010 17:54:35 +0000
parents 1928f9b408e6
children fdca34c989c0
files changeset.cpp changeset.h filestatuswidget.cpp filestatuswidget.h grapher.cpp hgtabwidget.cpp hgtabwidget.h mainwindow.cpp mainwindow.h
diffstat 9 files changed, 305 insertions(+), 241 deletions(-) [+]
line wrap: on
line diff
--- a/changeset.cpp	Thu Nov 25 17:21:32 2010 +0000
+++ b/changeset.cpp	Thu Nov 25 17:54:35 2010 +0000
@@ -16,3 +16,21 @@
 */
 
 #include "changeset.h"
+
+#include <QVariant>
+
+Changeset::Changeset(const LogEntry &e)
+{
+    foreach (QString key, e.keys()) {
+        if (key == "parents") {
+            QStringList parents = e.value(key).split
+                (" ", QString::SkipEmptyParts);
+            setParents(parents);
+        } else if (key == "timestamp") {
+            setTimestamp(e.value(key).split(" ")[0].toULongLong());
+        } else {
+            setProperty(key.toLocal8Bit().data(), e.value(key));
+        }
+    }
+}
+
--- a/changeset.h	Thu Nov 25 17:21:32 2010 +0000
+++ b/changeset.h	Thu Nov 25 17:54:35 2010 +0000
@@ -22,6 +22,9 @@
 #include <QString>
 #include <QStringList>
 #include <QList>
+#include <QSharedPointer>
+
+#include "logparser.h"
 
 class Changeset : public QObject
 {
@@ -40,6 +43,7 @@
 
 public:
     Changeset() : QObject() { }
+    explicit Changeset(const LogEntry &e);
 
     QString id() const { return m_id; }
     QString author() const { return m_author; }
--- a/filestatuswidget.cpp	Thu Nov 25 17:21:32 2010 +0000
+++ b/filestatuswidget.cpp	Thu Nov 25 17:54:35 2010 +0000
@@ -47,8 +47,15 @@
     m_remoteURLLabel = new QLabel;
     layout->addWidget(m_remoteURLLabel, row, 1);
 
+    ++row;
+    layout->addWidget(new QLabel(tr("Branch:")), row, 0);
+    m_branchLabel = new QLabel;
+    layout->addWidget(m_branchLabel, row, 1);
+
     layout->setColumnStretch(1, 20);
 
+    layout->addWidget(new QLabel("<qt><hr></qt>"), ++row, 0, 1, 2);
+
     m_simpleLabels[FileStates::Clean] = tr("Unmodified:");
     m_simpleLabels[FileStates::Modified] = tr("Modified:");
     m_simpleLabels[FileStates::Added] = tr("Added:");
@@ -79,7 +86,7 @@
         boxlayout->setMargin(0);
         box->setLayout(boxlayout);
 
-        boxlayout->addItem(new QSpacerItem(5, 8), 0, 0);
+        boxlayout->addItem(new QSpacerItem(5, 5), 0, 0);
 
         boxlayout->addWidget(new QLabel(labelFor(s)), 1, 0);
 
@@ -268,6 +275,13 @@
 }
 
 void
+FileStatusWidget::setBranch(QString b)
+{
+    m_branch = b;
+    m_branchLabel->setText(b);
+}
+
+void
 FileStatusWidget::updateWidgets()
 {
     QDateTime lastInteractionTime;
--- a/filestatuswidget.h	Thu Nov 25 17:21:32 2010 +0000
+++ b/filestatuswidget.h	Thu Nov 25 17:54:35 2010 +0000
@@ -40,6 +40,9 @@
     QString remoteURL() const { return m_remoteURL; }
     void setRemoteURL(QString u);
 
+    QString branch() const { return m_branch; }
+    void setBranch(QString b);
+
     FileStates fileStates() const { return m_fileStates; }
     void setFileStates(FileStates sp);
 
@@ -72,7 +75,10 @@
 
     QString m_remoteURL;
     QLabel *m_remoteURLLabel;
-    
+
+    QString m_branch;
+    QLabel *m_branchLabel;
+
     FileStates m_fileStates;
     QMap<FileStates::State, QString> m_simpleLabels;
     QMap<FileStates::State, QString> m_descriptions;
--- a/grapher.cpp	Thu Nov 25 17:21:32 2010 +0000
+++ b/grapher.cpp	Thu Nov 25 17:54:35 2010 +0000
@@ -23,40 +23,38 @@
 
 #include <iostream>
 
-int
-Grapher::findAvailableColumn(int row, int parent, bool preferParentCol)
+int Grapher::findAvailableColumn(int row, int parent, bool preferParentCol)
 {
     int col = parent;
     if (preferParentCol) {
-	if (!m_alloc[row].contains(col)) {
-	    return col;
-	}
+        if (!m_alloc[row].contains(col)) {
+            return col;
+        }
     }
     while (col > 0) {
-	if (!m_alloc[row].contains(--col)) return col;
+        if (!m_alloc[row].contains(--col)) return col;
     }
     while (col < 0) {
-	if (!m_alloc[row].contains(++col)) return col;
+        if (!m_alloc[row].contains(++col)) return col;
     }
     col = parent;
     int sign = (col < 0 ? -1 : 1);
     while (1) {
-	col += sign;
-	if (!m_alloc[row].contains(col)) return col;
+        col += sign;
+        if (!m_alloc[row].contains(col)) return col;
     }
 }
 
-void
-Grapher::layoutRow(QString id)
+void Grapher::layoutRow(QString id)
 {
     if (m_handled.contains(id)) {
-	return;
+        return;
     }
     if (!m_changesets.contains(id)) {
-	throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
+        throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
     }
     if (!m_items.contains(id)) {
-	throw LayoutException(QString("Changeset %1 not in item map").arg(id));
+        throw LayoutException(QString("Changeset %1 not in item map").arg(id));
     }
     Changeset *cs = m_changesets[id];
     ChangesetItem *item = m_items[id];
@@ -66,23 +64,23 @@
     int nparents = cs->parents().size();
 
     if (nparents > 0) {
-	bool haveRow = false;
-	foreach (QString parentId, cs->parents()) {
+        bool haveRow = false;
+        foreach (QString parentId, cs->parents()) {
 
-	    if (!m_changesets.contains(parentId)) continue;
-	    if (!m_items.contains(parentId)) continue;
+            if (!m_changesets.contains(parentId)) continue;
+            if (!m_items.contains(parentId)) continue;
 
-	    if (!m_handled.contains(parentId)) {
-		layoutRow(parentId);
-	    }
+            if (!m_handled.contains(parentId)) {
+                layoutRow(parentId);
+            }
 
-	    ChangesetItem *parentItem = m_items[parentId];
-	    if (!haveRow || parentItem->row() < row) {
-		row = parentItem->row();
-		haveRow = true;
-	    }
-	}
-	row = row - 1;
+            ChangesetItem *parentItem = m_items[parentId];
+            if (!haveRow || parentItem->row() < row) {
+                row = parentItem->row();
+                haveRow = true;
+            }
+        }
+        row = row - 1;
     }
 
     // row is now an upper bound on our eventual row (because we want
@@ -92,7 +90,7 @@
 
     QString date = cs->age();
     while (m_rowDates.contains(row) && m_rowDates[row] != date) {
-	--row;
+        --row;
     }
 
     // We have already laid out all nodes that have earlier timestamps
@@ -101,28 +99,27 @@
     // the map yet (it cannot have an earlier date)
 
     if (!m_rowDates.contains(row)) {
-	m_rowDates[row] = date;
+        m_rowDates[row] = date;
     }
 
     std::cerr << "putting " << cs->id().toStdString() << " at row " << row 
-	      << std::endl;
+            << std::endl;
 
     item->setRow(row);
     m_handled.insert(id);
 }
 
-void
-Grapher::layoutCol(QString id)
+void Grapher::layoutCol(QString id)
 {
     if (m_handled.contains(id)) {
-	std::cerr << "Already looked at " << id.toStdString() << std::endl;
-	return;
+        std::cerr << "Already looked at " << id.toStdString() << std::endl;
+        return;
     }
     if (!m_changesets.contains(id)) {
-	throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
+        throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
     }
     if (!m_items.contains(id)) {
-	throw LayoutException(QString("Changeset %1 not in item map").arg(id));
+        throw LayoutException(QString("Changeset %1 not in item map").arg(id));
     }
 
     Changeset *cs = m_changesets[id];
@@ -141,46 +138,46 @@
     switch (nparents) {
 
     case 0:
-	col = m_branchHomes[cs->branch()];
-	col = findAvailableColumn(row, col, true);
-	break;
+        col = m_branchHomes[cs->branch()];
+        col = findAvailableColumn(row, col, true);
+        break;
 
     case 1:
-	parentId = cs->parents()[0];
+        parentId = cs->parents()[0];
 
-	if (!m_changesets.contains(parentId) ||
-	    m_changesets[parentId]->branch() != branch) {
-	    // new branch
-	    col = m_branchHomes[branch];
-	} else {
-	    col = m_items[parentId]->column();
-	}
+        if (!m_changesets.contains(parentId) ||
+            m_changesets[parentId]->branch() != branch) {
+            // new branch
+            col = m_branchHomes[branch];
+        } else {
+            col = m_items[parentId]->column();
+        }
 
-	col = findAvailableColumn(row, col, true);
-	break;
+        col = findAvailableColumn(row, col, true);
+        break;
 
     case 2:
-	// a merge: behave differently if parents are both on the same
-	// branch (we also want to behave differently for nodes that
-	// have multiple children on the same branch -- spreading them
-	// out rather than having affinity to a specific branch)
+        // a merge: behave differently if parents are both on the same
+        // branch (we also want to behave differently for nodes that
+        // have multiple children on the same branch -- spreading them
+        // out rather than having affinity to a specific branch)
 
-	foreach (QString parentId, cs->parents()) {
-	    if (!m_changesets.contains(parentId)) continue;
-	    if (m_changesets[parentId]->branch() == branch) {
-		ChangesetItem *parentItem = m_items[parentId];
-		col += parentItem->column();
-		parentsOnSameBranch++;
-	    }
-	}
+        foreach (QString parentId, cs->parents()) {
+            if (!m_changesets.contains(parentId)) continue;
+            if (m_changesets[parentId]->branch() == branch) {
+                ChangesetItem *parentItem = m_items[parentId];
+                col += parentItem->column();
+                parentsOnSameBranch++;
+            }
+        }
 
-	if (parentsOnSameBranch > 0) {
-	    col /= parentsOnSameBranch;
-	    col = findAvailableColumn(item->row(), col, true);
-	} else {
-	    col = findAvailableColumn(item->row(), col, false);
-	}
-	break;
+        if (parentsOnSameBranch > 0) {
+            col /= parentsOnSameBranch;
+            col = findAvailableColumn(item->row(), col, true);
+        } else {
+            col = findAvailableColumn(item->row(), col, false);
+        }
+        break;
     }
 
     std::cerr << "putting " << cs->id().toStdString() << " at col " << col << std::endl;
@@ -201,13 +198,13 @@
     foreach (QString childId, cs->children()) {
         if (!m_changesets.contains(childId)) continue;
         Changeset *child = m_changesets[childId];
-	int childRow = m_items[childId]->row();
+        int childRow = m_items[childId]->row();
         if (child->parents().size() > 1 ||
-	    child->branch() == cs->branch()) {
+            child->branch() == cs->branch()) {
             for (int r = row-1; r > childRow; --r) {
                 m_alloc[r].insert(col);
             }
-	}	    
+        }
     }
 
     // look for the case where exactly two children have the same
@@ -215,31 +212,30 @@
 
     if (nchildren > 1) {
 
-	QList<QString> special;
-	foreach (QString childId, cs->children()) {
-	    if (!m_changesets.contains(childId)) continue;
-	    Changeset *child = m_changesets[childId];
-	    if (child->branch() == branch &&
-		child->parents().size() == 1) {
-		special.push_back(childId);
-	    }
-	}
-	if (special.size() == 2) {
-	    for (int i = 0; i < 2; ++i) {
-		int off = i * 2 - 1; // 0 -> -1, 1 -> 1
-		ChangesetItem *it = m_items[special[i]];
-		it->setColumn(findAvailableColumn(it->row(), col + off, true));
-		for (int r = row-1; r >= it->row(); --r) {
-		    m_alloc[r].insert(it->column());
-		}
-		m_handled.insert(special[i]);
-	    }
-	}
+        QList<QString> special;
+        foreach (QString childId, cs->children()) {
+            if (!m_changesets.contains(childId)) continue;
+            Changeset *child = m_changesets[childId];
+            if (child->branch() == branch &&
+                child->parents().size() == 1) {
+                special.push_back(childId);
+            }
+        }
+        if (special.size() == 2) {
+            for (int i = 0; i < 2; ++i) {
+                int off = i * 2 - 1; // 0 -> -1, 1 -> 1
+                ChangesetItem *it = m_items[special[i]];
+                it->setColumn(findAvailableColumn(it->row(), col + off, true));
+                for (int r = row-1; r >= it->row(); --r) {
+                    m_alloc[r].insert(it->column());
+                }
+                m_handled.insert(special[i]);
+            }
+        }
     }
 }
 
-bool
-Grapher::rangesConflict(const Range &r1, const Range &r2)
+bool Grapher::rangesConflict(const Range &r1, const Range &r2)
 {
     // allow some additional space at edges.  we really ought also to
     // permit space at the end of a branch up to the point where the
@@ -251,79 +247,77 @@
     return true;
 }
 
-void
-Grapher::allocateBranchHomes(Changesets csets)
+void Grapher::allocateBranchHomes(Changesets csets)
 {
     foreach (Changeset *cs, csets) {
-	QString branch = cs->branch();
-	ChangesetItem *item = m_items[cs->id()];
-	if (!item) continue;
-	int row = item->row();
-	if (!m_branchRanges.contains(branch)) {
-	    m_branchRanges[branch] = Range(row, row);
-	} else {
-	    Range p = m_branchRanges[branch];
-	    if (row < p.first) p.first = row;
-	    if (row > p.second) p.second = row;
-	    m_branchRanges[branch] = p;
-	}
+        QString branch = cs->branch();
+        ChangesetItem *item = m_items[cs->id()];
+        if (!item) continue;
+        int row = item->row();
+        if (!m_branchRanges.contains(branch)) {
+            m_branchRanges[branch] = Range(row, row);
+        } else {
+            Range p = m_branchRanges[branch];
+            if (row < p.first) p.first = row;
+            if (row > p.second) p.second = row;
+            m_branchRanges[branch] = p;
+        }
     }
 
     m_branchHomes[""] = 0;
 
     foreach (QString branch, m_branchRanges.keys()) {
-	if (branch == "") continue;
-	QSet<int> taken;
-	taken.insert(0);
-	Range myRange = m_branchRanges[branch];
-	foreach (QString other, m_branchRanges.keys()) {
-	    if (other == branch || other == "") continue;
-	    Range otherRange = m_branchRanges[other];
-	    if (rangesConflict(myRange, otherRange)) {
-		if (m_branchHomes.contains(other)) {
-		    taken.insert(m_branchHomes[other]);
-		}
-	    }
-	}
-	int home = 2;
-	while (taken.contains(home)) {
-	    if (home > 0) {
-		if (home % 2 == 1) {
-		    home = -home;
-		} else {
-		    home = home + 1;
-		}
-	    } else {
-		if ((-home) % 2 == 1) {
-		    home = home + 1;
-		} else {
-		    home = -(home-2);
-		}
-	    }
-	}
-	m_branchHomes[branch] = home;
+        if (branch == "") continue;
+        QSet<int> taken;
+        taken.insert(0);
+        Range myRange = m_branchRanges[branch];
+        foreach (QString other, m_branchRanges.keys()) {
+            if (other == branch || other == "") continue;
+            Range otherRange = m_branchRanges[other];
+            if (rangesConflict(myRange, otherRange)) {
+                if (m_branchHomes.contains(other)) {
+                    taken.insert(m_branchHomes[other]);
+                }
+            }
+        }
+        int home = 2;
+        while (taken.contains(home)) {
+            if (home > 0) {
+                if (home % 2 == 1) {
+                    home = -home;
+                } else {
+                    home = home + 1;
+                }
+            } else {
+                if ((-home) % 2 == 1) {
+                    home = home + 1;
+                } else {
+                    home = -(home-2);
+                }
+            }
+        }
+        m_branchHomes[branch] = home;
     }
 
     foreach (QString branch, m_branchRanges.keys()) {
-	std::cerr << branch.toStdString() << ": " << m_branchRanges[branch].first << " - " << m_branchRanges[branch].second << ", home " << m_branchHomes[branch] << std::endl;
+        std::cerr << branch.toStdString() << ": " << m_branchRanges[branch].first << " - " << m_branchRanges[branch].second << ", home " << m_branchHomes[branch] << std::endl;
     }
 }
 
 static bool
-compareChangesetsByDate(Changeset *const &a, Changeset *const &b)
+        compareChangesetsByDate(Changeset *const &a, Changeset *const &b)
 {
     return a->timestamp() < b->timestamp();
 }
 
 ChangesetItem *
-Grapher::getItemFor(Changeset *cs)
+        Grapher::getItemFor(Changeset *cs)
 {
     if (!cs || !m_items.contains(cs->id())) return 0;
     return m_items[cs->id()];
 }
 
-void
-Grapher::layout(Changesets csets)
+void Grapher::layout(Changesets csets)
 {
     m_changesets.clear();
     m_items.clear();
@@ -334,41 +328,41 @@
 
     foreach (Changeset *cs, csets) {
 
-	QString id = cs->id();
-	std::cerr << id.toStdString() << std::endl;
+        QString id = cs->id();
+        std::cerr << id.toStdString() << std::endl;
 
-	if (id == "") {
-	    throw LayoutException("Changeset has no ID");
-	}
-	if (m_changesets.contains(id)) {
-	    throw LayoutException(QString("Duplicate changeset ID %1").arg(id));
-	}
+        if (id == "") {
+            throw LayoutException("Changeset has no ID");
+        }
+        if (m_changesets.contains(id)) {
+            throw LayoutException(QString("Duplicate changeset ID %1").arg(id));
+        }
 
-	m_changesets[id] = cs;
+        m_changesets[id] = cs;
 
         ChangesetItem *item = new ChangesetItem(cs);
         item->setX(0);
         item->setY(0);
-	m_items[id] = item;
+        m_items[id] = item;
         m_scene->addItem(item);
     }
 
     // Add the connecting lines
 
     foreach (Changeset *cs, csets) {
-	QString id = cs->id();
-	ChangesetItem *item = m_items[id];
-	bool merge = (cs->parents().size() > 1);
-	foreach (QString parentId, cs->parents()) {
-	    if (!m_changesets.contains(parentId)) continue;
-	    Changeset *parent = m_changesets[parentId];
-	    parent->addChild(id);
-	    ConnectionItem *conn = new ConnectionItem();
-	    if (merge) conn->setConnectionType(ConnectionItem::Merge);
-	    conn->setChild(item);
-	    conn->setParent(m_items[parentId]);
-	    m_scene->addItem(conn);
-	}
+        QString id = cs->id();
+        ChangesetItem *item = m_items[id];
+        bool merge = (cs->parents().size() > 1);
+        foreach (QString parentId, cs->parents()) {
+            if (!m_changesets.contains(parentId)) continue;
+            Changeset *parent = m_changesets[parentId];
+            parent->addChild(id);
+            ConnectionItem *conn = new ConnectionItem();
+            if (merge) conn->setConnectionType(ConnectionItem::Merge);
+            conn->setChild(item);
+            conn->setParent(m_items[parentId]);
+            m_scene->addItem(conn);
+        }
     }
 
     // Add the branch labels
@@ -396,33 +390,33 @@
     qStableSort(csets.begin(), csets.end(), compareChangesetsByDate);
 
     foreach (Changeset *cs, csets) {
-	std::cerr << "id " << cs->id().toStdString() << ", ts " << cs->timestamp() << ", date " << cs->datetime().toStdString() << std::endl;
+        std::cerr << "id " << cs->id().toStdString() << ", ts " << cs->timestamp() << ", date " << cs->datetime().toStdString() << std::endl;
     }
 
     m_handled.clear();
     foreach (Changeset *cs, csets) {
-	layoutRow(cs->id());
+        layoutRow(cs->id());
     }
 
     allocateBranchHomes(csets);
 
     m_handled.clear();
     foreach (Changeset *cs, csets) {
-	foreach (QString parentId, cs->parents()) {
-	    if (!m_handled.contains(parentId) &&
-		m_changesets.contains(parentId)) {
-		layoutCol(parentId);
-	    }
-	}
-	layoutCol(cs->id());
+        foreach (QString parentId, cs->parents()) {
+            if (!m_handled.contains(parentId) &&
+                m_changesets.contains(parentId)) {
+                layoutCol(parentId);
+            }
+        }
+        layoutCol(cs->id());
     }
 
     foreach (Changeset *cs, csets) {
-	ChangesetItem *item = m_items[cs->id()];
-	if (!m_alloc[item->row()].contains(item->column()-1) &&
-	    !m_alloc[item->row()].contains(item->column()+1)) {
-	    item->setWide(true);
-	}
+        ChangesetItem *item = m_items[cs->id()];
+        if (!m_alloc[item->row()].contains(item->column()-1) &&
+            !m_alloc[item->row()].contains(item->column()+1)) {
+            item->setWide(true);
+        }
     }
 
     // we know that 0 is an upper bound on row, and that mincol must
@@ -431,13 +425,13 @@
     int mincol = 0, maxcol = 0;
 
     foreach (int r, m_alloc.keys()) {
-	if (r < minrow) minrow = r;
-	if (r > maxrow) maxrow = r;
-	ColumnSet &c = m_alloc[r];
-	foreach (int i, c) {
-	    if (i < mincol) mincol = i;
-	    if (i > maxcol) maxcol = i;
-	}
+        if (r < minrow) minrow = r;
+        if (r > maxrow) maxrow = r;
+        ColumnSet &c = m_alloc[r];
+        foreach (int i, c) {
+            if (i < mincol) mincol = i;
+            if (i > maxcol) maxcol = i;
+        }
     }
 
     QString prevDate;
@@ -447,36 +441,36 @@
     int n = 0;
 
     for (int row = minrow; row <= maxrow; ++row) {
-	
-	QString date = m_rowDates[row];
-	n++;
 
-	if (date != prevDate) {
-	    if (prevDate != "") {
-		DateItem *item = new DateItem();
-		item->setDateString(prevDate);
-		item->setCols(mincol, maxcol - mincol + 1);
-		item->setRows(changeRow, n);
-		item->setEven(even);
-		item->setZValue(-1);
-		m_scene->addItem(item);
-		even = !even;
-	    }
-	    prevDate = date;
-	    changeRow = row;
-	    n = 0;
-	}
+        QString date = m_rowDates[row];
+        n++;
+
+        if (date != prevDate) {
+            if (prevDate != "") {
+                DateItem *item = new DateItem();
+                item->setDateString(prevDate);
+                item->setCols(mincol, maxcol - mincol + 1);
+                item->setRows(changeRow, n);
+                item->setEven(even);
+                item->setZValue(-1);
+                m_scene->addItem(item);
+                even = !even;
+            }
+            prevDate = date;
+            changeRow = row;
+            n = 0;
+        }
     }
     
     if (n > 0) {
-	DateItem *item = new DateItem();
-	item->setDateString(prevDate);
-	item->setCols(mincol, maxcol - mincol + 1);
-	item->setRows(changeRow, n+1);
-	item->setEven(even);
-	item->setZValue(-1);
-	m_scene->addItem(item);
-	even = !even;
+        DateItem *item = new DateItem();
+        item->setDateString(prevDate);
+        item->setCols(mincol, maxcol - mincol + 1);
+        item->setRows(changeRow, n+1);
+        item->setEven(even);
+        item->setZValue(-1);
+        m_scene->addItem(item);
+        even = !even;
     }
 }
 
--- a/hgtabwidget.cpp	Thu Nov 25 17:21:32 2010 +0000
+++ b/hgtabwidget.cpp	Thu Nov 25 17:54:35 2010 +0000
@@ -34,7 +34,7 @@
 HgTabWidget::HgTabWidget(QWidget *parent,
                          QString remoteRepo,
                          QString workFolderPath) :
-    QTabWidget(parent)
+QTabWidget(parent)
 {
     // Work page
     fileStatusWidget = new FileStatusWidget;
@@ -140,9 +140,9 @@
     if (csets.empty()) return;
     Grapher g(scene);
     try {
-	g.layout(csets);
+        g.layout(csets);
     } catch (std::string s) {
-	std::cerr << "Internal error: Layout failed: " << s << std::endl;
+        std::cerr << "Internal error: Layout failed: " << s << std::endl;
     }
     QGraphicsScene *oldScene = panned->scene();
     panned->setScene(scene);
@@ -157,27 +157,16 @@
     Changesets csets;
     LogList log = LogParser(changeSetsStr).parse();
     foreach (LogEntry e, log) {
-        Changeset *cs = new Changeset();
-        foreach (QString key, e.keys()) {
-	    if (key == "parents") {
-		QStringList parents = e.value(key).split
-		    (" ", QString::SkipEmptyParts);
-		cs->setParents(parents);
-	    } else if (key == "timestamp") {
-		cs->setTimestamp(e.value(key).split(" ")[0].toULongLong());
-	    } else {
-		cs->setProperty(key.toLocal8Bit().data(), e.value(key));
-	    }
-        }
+        Changeset *cs = new Changeset(e);
         csets.push_back(cs);
     }
     for (int i = 0; i+1 < csets.size(); ++i) {
-	Changeset *cs = csets[i];
-	if (cs->parents().empty()) {
-	    QStringList list;
-	    list.push_back(csets[i+1]->id());
-	    cs->setParents(list);
-	}
+        Changeset *cs = csets[i];
+        if (cs->parents().empty()) {
+            QStringList list;
+            list.push_back(csets[i+1]->id());
+            cs->setParents(list);
+        }
     }
     return csets;
 }
@@ -187,3 +176,8 @@
     fileStatusWidget->setLocalPath(workFolderPath);
     fileStatusWidget->setRemoteURL(remoteRepoPath);
 }
+
+void HgTabWidget::setBranch(QString branch)
+{
+    fileStatusWidget->setBranch(branch);
+}
--- a/hgtabwidget.h	Thu Nov 25 17:21:32 2010 +0000
+++ b/hgtabwidget.h	Thu Nov 25 17:54:35 2010 +0000
@@ -43,6 +43,7 @@
     void updateWorkFolderFileList(QString fileList);
     void updateLocalRepoHgLogList(QString hgLogList);
     void setWorkFolderAndRepoNames(QString workFolderPath, QString remoteRepoPath);
+    void setBranch(QString branch);
 
     FileStates getFileStates() { return fileStates; }
 
--- a/mainwindow.cpp	Thu Nov 25 17:21:32 2010 +0000
+++ b/mainwindow.cpp	Thu Nov 25 17:54:35 2010 +0000
@@ -169,6 +169,17 @@
     }
 }
 
+void MainWindow::hgBranch()
+{
+    if (runningAction == ACT_NONE)
+    {
+        QStringList params;
+        params << "branch";
+        runner -> startHgCommand(workFolderPath, params);
+        runningAction = ACT_BRANCH;
+    }
+}
+
 void MainWindow::hgHeads()
 {
     if (runningAction == ACT_NONE)
@@ -1132,6 +1143,11 @@
                         break;
                     }
 
+                    case ACT_BRANCH:
+                        currentBranch = runner->getOutput().trimmed();
+                        hgTabs->setBranch(currentBranch);
+                        break;
+
                     case ACT_STAT:
                         hgTabs -> updateWorkFolderFileList(runner -> getOutput());
                         updateFileSystemWatcher();
@@ -1183,8 +1199,14 @@
 
                     case ACT_HEADS:
                         {
-                            QString stdOut = runner -> getOutput();
-                        //!!!    hgTabs -> updateLocalRepoHeadsList(stdOut);
+                            foreach (Changeset *cs, currentHeads) delete cs;
+                            currentHeads.clear();
+                            QString output = runner -> getOutput();
+                            DEBUG << "heads output is: " << output << endl;
+                            LogList log = LogParser(output).parse();
+                            foreach (LogEntry e, log) {
+                                currentHeads.push_back(new Changeset(e));
+                            }
                         }
                         break;
 
@@ -1224,10 +1246,15 @@
             }
 
 
-            //Typical sequence goes paths -> stat -> heads -> parents -> log
+            //Typical sequence goes paths -> branch -> stat -> heads -> parents -> log
             if (runningAction == ACT_PATHS)
             {
                 runningAction = ACT_NONE;
+                hgBranch();
+            }
+            else if (runningAction == ACT_BRANCH)
+            {
+                runningAction = ACT_NONE;
                 hgStat();
             }
             else if (runningAction == ACT_STAT)
@@ -1238,7 +1265,8 @@
             else if (runningAction == ACT_HEADS)
             {
                 runningAction = ACT_NONE;
-                hgParents();
+//!!!                hgParents(); // skip this, we don't currently use it
+                hgLog();
             }
             else if (runningAction == ACT_PARENTS)
             {
@@ -1247,7 +1275,7 @@
             }
             else if ((runningAction == ACT_MERGE) && (exitCode != 0))
             {
-                //If we had a failed merge, offer to retry
+                // If we had a failed merge, offer to retry
                 if (QMessageBox::Ok == QMessageBox::information(this, tr("Retry merge ?"), tr("Merge attempt failed. retry ?"), QMessageBox::Ok | QMessageBox::Cancel))
                 {
                     runningAction = ACT_NONE;
--- a/mainwindow.h	Thu Nov 25 17:21:32 2010 +0000
+++ b/mainwindow.h	Thu Nov 25 17:54:35 2010 +0000
@@ -21,6 +21,7 @@
 #include "hgtabwidget.h"
 #include "hgrunner.h"
 #include "common.h"
+#include "changeset.h"
 
 #include <QMainWindow>
 #include <QListWidget>
@@ -35,6 +36,7 @@
 {
     ACT_NONE,
     ACT_PATHS,
+    ACT_BRANCH,
     ACT_STAT,
     ACT_HEADS,
     ACT_PARENTS,
@@ -76,6 +78,8 @@
     //Local repo is directory "./hg/" under work folder
     QString remoteRepoPath;
     QString workFolderPath;
+    QString currentBranch;
+    Changesets currentHeads;
 
 protected:
     void closeEvent(QCloseEvent *event);
@@ -122,6 +126,7 @@
     void fsFileChanged(QString);
 
 private:
+    void hgBranch();
     void hgHeads();
     void hgParents();
     void hgLog();