UNPKG

4.86 kBJavaScriptView Raw
1var fs = require('fs')
2var u = require('./util')
3var multicb = require('multicb')
4var Mentions = require('ssb-mentions')
5
6exports.help = `
7Usage: git ssb pull-request [-b <base>] [-h <head>],
8 [-m <message> | -F <file>]
9
10 Create a pull request. This requests that changes from <head>
11 be merged into <base>.
12
13Arguments:
14 head the head repo/branch, in format "[<repo>:]<branch>"
15 Defaults to 'origin' or 'ssb', and the current branch.
16 base the base repo/branch, in format "[<repo>:]<branch>"
17 where <repo> may be a repo id or git remote name.
18 Defaults to the upstream of <head>, or <head>,
19 and its default branch (usually 'master')
20 message the text for the pull-request message
21 file name of file from which to read pull-request text
22`
23
24function splitEnd(str) {
25 return str ? /(?:(.*?):)?(.*)/.exec(str).slice(1) : []
26}
27
28function mdLink(text, href) {
29 return !text || text == href ? href : '[' + text + '](' + href + ')'
30}
31
32function getRev(repo, branch, cb) {
33 var Repo = require('pull-git-repo')
34 Repo(repo).resolveRef(branch, function (err, rev) {
35 if (err && err.name === 'NotFoundError') err = null
36 cb(null, rev)
37 })
38}
39
40function formatGitLog(a, b) {
41 var range = a + '..' + b
42 try {
43 // https://github.com/github/hub/blob/master/git/git.go
44 return u.gitSync('log', '--no-color', '--cherry',
45 '--format=%h (%aN, %ar)%n%w(78,3,3)%s%n%+b', range)
46 } catch(e) {
47 return '`git log ' + range + '`'
48 }
49}
50
51exports.fn = function pullRequest(argv) {
52 if (argv._.length > 0) return u.help('pull-request')
53
54 var head = splitEnd(argv.head || argv.h)
55 var headRepoId = u.getRemote(head[0])
56 var headBranch = head[1] || u.getCurrentBranch()
57 if (!headRepoId || !headBranch) throw 'unable to find head'
58
59 var text = argv.message || argv.m
60 var filename = argv.file || argv.F
61 if (text && filename)
62 throw 'only one of message and file option may be specified'
63 if (filename && !fs.existsSync(filename))
64 throw 'file ' + JSON.stringify(filename) + ' does not exist'
65
66 var base = splitEnd(argv.base || argv.b)
67 var baseRepoId = base[0]
68 var baseBranch = base[1]
69 if (baseRepoId) {
70 baseRepoId = u.getRemote(baseRepoId)
71 if (!baseRepoId) throw 'invalid base repo ' + JSON.stringify(base[0])
72 }
73
74 var ssbGit = require('ssb-git-repo')
75 var sbot
76 var done = multicb({pluck: 1, spread: true})
77 var gotFeed = done()
78 var gotHeadRepo = done()
79 var gotBaseRepo = done()
80 u.getSbot(argv, function (err, _sbot) {
81 if (err) throw err
82 sbot = _sbot
83 sbot.whoami(gotFeed)
84 ssbGit.getRepo(sbot, headRepoId, gotHeadRepo)
85 if (baseRepoId) ssbGit.getRepo(sbot, baseRepoId, gotBaseRepo)
86 else gotBaseRepo()
87 })
88
89 done(function (err, feed, headRepo, baseRepo) {
90 if (err) throw err
91 sbot.id = feed.id
92
93 // default base repo to upstream of head repo, or head repo
94 // default base branch to base repo's default branch
95 if (!baseRepo) {
96 if (headRepo.upstream) {
97 baseRepo = headRepo.upstream
98 } else {
99 baseRepo = headRepo
100 }
101 baseRepoId = baseRepo.id
102 }
103
104 if (baseBranch) next()
105 else baseRepo.getHead(function (err, ref) {
106 if (err) throw err
107 baseBranch = ref && ref.replace(/refs\/heads\//, '') || 'master'
108 next()
109 })
110
111 function next() {
112 if (text) gotText(text, doneEditing)
113 else if (filename) fs.readFile(filename, 'utf8', gotText)
114 else {
115 var done = multicb({pluck: 1, spread: true})
116 u.getName(sbot, [sbot.id, null], baseRepoId, done())
117 u.getName(sbot, [sbot.id, null], headRepoId, done())
118 getRev(baseRepo, baseBranch, done())
119 getRev(headRepo, headBranch, done())
120 done(editText)
121 }
122 }
123 })
124
125 function editText(err, baseRepoName, headRepoName, baseRev, headRev) {
126 if (err) throw err
127 var defaultText = 'Requesting a pull ' +
128 'to ' + mdLink(baseRepoName, baseRepoId) + ':' + baseBranch + '\n' +
129 'from ' + mdLink(headRepoName, headRepoId) + ':' + headBranch + '\n\n' +
130 'Write message text for this pull request.' +
131 (baseRev && headRev
132 ? '\n\nChanges:\n\n' + formatGitLog(baseRev, headRev)
133 : '')
134 u.editor('PULLREQ', defaultText, gotText, doneEditing)
135 }
136
137 function gotText(text, cb) {
138 if (!text) return cb('empty message: aborting')
139 var prSchemas = require('ssb-pull-requests/lib/schemas')
140 var value = prSchemas.new(baseRepoId, baseBranch,
141 headRepoId, headBranch, text)
142 var mentions = Mentions(text)
143 if (mentions.length) value.mentions = mentions
144 sbot.publish(value, function (err, msg) {
145 if (err) return cb(err)
146 console.log(JSON.stringify(msg, 0, 2))
147 cb(null)
148 })
149 }
150
151 function doneEditing(err) {
152 if (err) console.error(err)
153 sbot.close()
154 }
155}