From 72d76e511eb17ae3276546a3bf001d8ab9c5d163 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Kevin Sawicki Date: Wed, 10 Jul 2013 15:42:32 -0700 Subject: [PATCH] Begin replication of host repo state --- package.json | 2 +- src/app/git.coffee | 6 ++ .../collaboration/lib/bootstrap.coffee | 40 +++++++- .../collaboration/lib/guest-session.coffee | 7 +- .../collaboration/lib/host-session.coffee | 97 +++++++++++++------ 5 files changed, 115 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index c80db2ffd..c1bcad0d2 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "ctags": "0.5.0", "oniguruma": "0.16.0", "mkdirp": "0.3.5", - "git-utils": "0.19.0", + "git-utils": "0.21.0", "underscore": "1.4.4", "d3": "3.0.8", "coffee-cache": "0.1.0", diff --git a/src/app/git.coffee b/src/app/git.coffee index cc0eb2404..bffae7be7 100644 --- a/src/app/git.coffee +++ b/src/app/git.coffee @@ -215,6 +215,12 @@ class Git getConfigValue: (key) -> @getRepo().getConfigValue(key) + getReferenceTarget: (reference) -> @getRepo().getReferenceTarget(reference) + + getAheadBehindCount: (reference) -> @getRepo().getAheadBehindCount(reference) + + hasBranch: (branch) -> @getReferenceTarget("refs/heads/#{branch}")? + ### Internal ### refreshStatus: -> diff --git a/src/packages/collaboration/lib/bootstrap.coffee b/src/packages/collaboration/lib/bootstrap.coffee index 24b095daf..b9595dc48 100644 --- a/src/packages/collaboration/lib/bootstrap.coffee +++ b/src/packages/collaboration/lib/bootstrap.coffee @@ -1,10 +1,15 @@ +require 'atom' +require 'window' + +{exec} = require 'child_process' +fs = require 'fs' remote = require 'remote' path = require 'path' url = require 'url' -require 'atom' -require 'window' $ = require 'jquery' +temp = require 'temp' {$$} = require 'space-pen' +Git = require 'git' GuestSession = require './guest-session' window.setDimensions(width: 350, height: 100) @@ -19,14 +24,41 @@ atom.show() syncRepositoryState = -> repoUrl = atom.guestSession.repository.get('url') + branch = atom.guestSession.repository.get('branch') [repoName] = url.parse(repoUrl).path.split('/')[-1..] repoName = repoName.replace(/\.git$/, '') repoPath = path.join(remote.require('app').getHomeDir(), 'github', repoName) + git = new Git(repoPath) - # clone if missing + # clone or fetch # abort if working directory is unclean + # apply bundle of unpushed changes from host - # prompt for branch name if branch already exists and is cannot be fast-forwarded + {unpushedChanges, head} =atom.guestSession.repositoryDelta + if unpushedChanges + tempFile = temp.path(suffix: '.bundle') + fs.writeFileSync(tempFile, new Buffer(atom.guestSession.repositoryDelta.unpushedChanges, 'base64')) + command = "git bundle unbundle #{tempFile}" + exec command, {cwd: repoPath}, (error, stdout, stderr) -> + if error? + console.error error + return + + if git.hasBranch(branch) + if git.getAheadBehindCount(branch).ahead is 0 + command = "git checkout #{branch} && git reset --hard #{head}" + exec command, {cwd: repoPath}, (error, stdout, stderr) -> + if error? + console.error error + return + else + # prompt for new branch name + # create branch at head + else + # create branch at head + + # create branch if it doesn't exist + # prompt for branch name if branch already exists and it cannot be fast-forwarded # checkout branch # sync modified and untracked files from host session diff --git a/src/packages/collaboration/lib/guest-session.coffee b/src/packages/collaboration/lib/guest-session.coffee index 5f7ecd1a7..98c118ca0 100644 --- a/src/packages/collaboration/lib/guest-session.coffee +++ b/src/packages/collaboration/lib/guest-session.coffee @@ -16,13 +16,14 @@ class GuestSession connection.on 'open', => console.log 'connection opened' connection.once 'data', (data) => - console.log 'received document' - doc = telepath.Document.deserialize(data, site: telepath.createSite(@getId())) + console.log 'received document', data + @repositoryDelta = data.repositoryDelta + doc = telepath.Document.deserialize(data.doc, site: telepath.createSite(@getId())) atom.windowState = doc.get('windowState') @participants = doc.get('collaborationState.participants') @participants.on 'changed', => @trigger 'participants-changed', @participants.toObject() - @repository = doc.get('collaborationState.repository') + @repository = doc.get('collaborationState.repositoryState') connectDocument(doc, connection) @trigger 'started' diff --git a/src/packages/collaboration/lib/host-session.coffee b/src/packages/collaboration/lib/host-session.coffee index ccec3286d..c947d6bb6 100644 --- a/src/packages/collaboration/lib/host-session.coffee +++ b/src/packages/collaboration/lib/host-session.coffee @@ -1,4 +1,7 @@ +fs = require 'fs' _ = require 'underscore' +async = require 'async' +temp = require 'temp' telepath = require 'telepath' {createPeer, connectDocument} = require './session-utils' @@ -11,46 +14,82 @@ class HostSession peer: null sharing: false + bundleUnpushedChanges: (callback) -> + localBranch = git.getShortHead() + upstreamBranch = git.getRepo().getUpstreamBranch() + + {exec} = require 'child_process' + tempFile = temp.path(suffix: '.bundle') + command = "git bundle create #{tempFile} #{upstreamBranch}..#{localBranch}" + exec command, {cwd: git.getWorkingDirectory()}, (error, stdout, stderr) -> + callback(error, tempFile) + + bundleWorkingDirectoryChanges: -> + + + bundleRepositoryDelta: (callback) -> + repositoryDelta = {} + + operations = [] + if git.upstream.ahead > 0 + operations.push (callback) => + @bundleUnpushedChanges (error, bundleFile) -> + unless error? + repositoryDelta.unpushedChanges = fs.readFileSync(bundleFile, 'base64') + repositoryDelta.head = git.getRepo().getReferenceTarget(git.getRepo().getHead()) + callback(error) + + async.waterfall operations, (error) -> + callback(error, repositoryDelta) + + unless _.isEmpty(git.statuses) + repositoryDelta.workingDirectoryChanges = @bundleWorkingDirectoryChanges() + start: -> return if @peer? @peer = createPeer() @doc = telepath.Document.create({}, site: telepath.createSite(@getId())) @doc.set('windowState', atom.windowState) - @doc.set 'collaborationState', - participants: [] - repository: - url: git.getConfigValue('remote.origin.url') - branch: git.getShortHead() + @bundleRepositoryDelta (error, repositoryDelta) => + if error? + console.error(error) + return - @participants = @doc.get('collaborationState.participants') - @participants.push - id: @getId() - email: git.getConfigValue('user.email') - @participants.on 'changed', => - @trigger 'participants-changed', @participants.toObject() + @doc.set 'collaborationState', + participants: [] + repositoryState: + url: git.getConfigValue('remote.origin.url') + branch: git.getShortHead() - @peer.on 'connection', (connection) => - connection.on 'open', => - console.log 'sending document' - connection.send(@doc.serialize()) - connectDocument(@doc, connection) + @participants = @doc.get('collaborationState.participants') + @participants.push + id: @getId() + email: git.getConfigValue('user.email') + @participants.on 'changed', => + @trigger 'participants-changed', @participants.toObject() - connection.on 'close', => - console.log 'conection closed' - @participants.each (participant, index) => - if connection.peer is participant.get('id') - @participants.remove(index) + @peer.on 'connection', (connection) => + connection.on 'open', => + console.log 'sending document' + connection.send({repositoryDelta, doc: @doc.serialize()}) + connectDocument(@doc, connection) - @peer.on 'open', => - console.log 'sharing session started' - @sharing = true - @trigger 'started' + connection.on 'close', => + console.log 'conection closed' + @participants.each (participant, index) => + if connection.peer is participant.get('id') + @participants.remove(index) - @peer.on 'close', => - console.log 'sharing session stopped' - @sharing = false - @trigger 'stopped' + @peer.on 'open', => + console.log 'sharing session started' + @sharing = true + @trigger 'started' + + @peer.on 'close', => + console.log 'sharing session stopped' + @sharing = false + @trigger 'stopped' @getId()