You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo.go 27 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "errors"
  7. "fmt"
  8. "html"
  9. "html/template"
  10. "io/ioutil"
  11. "os"
  12. "os/exec"
  13. "path"
  14. "path/filepath"
  15. "regexp"
  16. "sort"
  17. "strings"
  18. "time"
  19. "unicode/utf8"
  20. "github.com/Unknwon/cae/zip"
  21. "github.com/Unknwon/com"
  22. "github.com/gogits/gogs/modules/git"
  23. "github.com/gogits/gogs/modules/log"
  24. "github.com/gogits/gogs/modules/process"
  25. "github.com/gogits/gogs/modules/setting"
  26. )
  27. const (
  28. TPL_UPDATE_HOOK = "#!/usr/bin/env %s\n%s update $1 $2 $3\n"
  29. )
  30. var (
  31. ErrRepoAlreadyExist = errors.New("Repository already exist")
  32. ErrRepoNotExist = errors.New("Repository does not exist")
  33. ErrRepoFileNotExist = errors.New("Repository file does not exist")
  34. ErrRepoNameIllegal = errors.New("Repository name contains illegal characters")
  35. ErrRepoFileNotLoaded = errors.New("Repository file not loaded")
  36. ErrMirrorNotExist = errors.New("Mirror does not exist")
  37. ErrInvalidReference = errors.New("Invalid reference specified")
  38. )
  39. var (
  40. Gitignores, Licenses []string
  41. )
  42. var (
  43. DescriptionPattern = regexp.MustCompile(`https?://\S+`)
  44. )
  45. func LoadRepoConfig() {
  46. // Load .gitignore and license files.
  47. types := []string{"gitignore", "license"}
  48. typeFiles := make([][]string, 2)
  49. for i, t := range types {
  50. files, err := com.StatDir(path.Join("conf", t))
  51. if err != nil {
  52. log.Fatal(4, "Fail to get %s files: %v", t, err)
  53. }
  54. customPath := path.Join(setting.CustomPath, "conf", t)
  55. if com.IsDir(customPath) {
  56. customFiles, err := com.StatDir(customPath)
  57. if err != nil {
  58. log.Fatal(4, "Fail to get custom %s files: %v", t, err)
  59. }
  60. for _, f := range customFiles {
  61. if !com.IsSliceContainsStr(files, f) {
  62. files = append(files, f)
  63. }
  64. }
  65. }
  66. typeFiles[i] = files
  67. }
  68. Gitignores = typeFiles[0]
  69. Licenses = typeFiles[1]
  70. sort.Strings(Gitignores)
  71. sort.Strings(Licenses)
  72. }
  73. func NewRepoContext() {
  74. zip.Verbose = false
  75. // Check Git installation.
  76. if _, err := exec.LookPath("git"); err != nil {
  77. log.Fatal(4, "Fail to test 'git' command: %v (forgotten install?)", err)
  78. }
  79. // Check Git version.
  80. ver, err := git.GetVersion()
  81. if err != nil {
  82. log.Fatal(4, "Fail to get Git version: %v", err)
  83. }
  84. if ver.Major < 2 && ver.Minor < 8 {
  85. log.Fatal(4, "Gogs requires Git version greater or equal to 1.8.0")
  86. }
  87. // Check if server has basic git setting.
  88. stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name")
  89. if err != nil {
  90. log.Fatal(4, "Fail to get git user.name: %s", stderr)
  91. } else if err != nil || len(strings.TrimSpace(stdout)) == 0 {
  92. if _, stderr, err = process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", "gogitservice@gmail.com"); err != nil {
  93. log.Fatal(4, "Fail to set git user.email: %s", stderr)
  94. } else if _, stderr, err = process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", "Gogs"); err != nil {
  95. log.Fatal(4, "Fail to set git user.name: %s", stderr)
  96. }
  97. }
  98. }
  99. // Repository represents a git repository.
  100. type Repository struct {
  101. Id int64
  102. OwnerId int64 `xorm:"UNIQUE(s)"`
  103. Owner *User `xorm:"-"`
  104. ForkId int64
  105. LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
  106. Name string `xorm:"INDEX NOT NULL"`
  107. Description string
  108. Website string
  109. NumWatches int
  110. NumStars int
  111. NumForks int
  112. NumIssues int
  113. NumClosedIssues int
  114. NumOpenIssues int `xorm:"-"`
  115. NumPulls int
  116. NumClosedPulls int
  117. NumOpenPulls int `xorm:"-"`
  118. NumMilestones int `xorm:"NOT NULL DEFAULT 0"`
  119. NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"`
  120. NumOpenMilestones int `xorm:"-"`
  121. NumTags int `xorm:"-"`
  122. IsPrivate bool
  123. IsMirror bool
  124. IsFork bool `xorm:"NOT NULL DEFAULT false"`
  125. IsBare bool
  126. IsGoget bool
  127. DefaultBranch string
  128. Created time.Time `xorm:"CREATED"`
  129. Updated time.Time `xorm:"UPDATED"`
  130. }
  131. func (repo *Repository) GetOwner() (err error) {
  132. repo.Owner, err = GetUserById(repo.OwnerId)
  133. return err
  134. }
  135. // DescriptionHtml does special handles to description and return HTML string.
  136. func (repo *Repository) DescriptionHtml() template.HTML {
  137. sanitize := func(s string) string {
  138. // TODO(nuss-justin): Improve sanitization. Strip all tags?
  139. ss := html.EscapeString(s)
  140. return fmt.Sprintf(`<a href="%s" target="_blank">%s</a>`, ss, ss)
  141. }
  142. return template.HTML(DescriptionPattern.ReplaceAllStringFunc(repo.Description, sanitize))
  143. }
  144. // IsRepositoryExist returns true if the repository with given name under user has already existed.
  145. func IsRepositoryExist(u *User, repoName string) (bool, error) {
  146. repo := Repository{OwnerId: u.Id}
  147. has, err := x.Where("lower_name = ?", strings.ToLower(repoName)).Get(&repo)
  148. if err != nil {
  149. return has, err
  150. } else if !has {
  151. return false, nil
  152. }
  153. return com.IsDir(RepoPath(u.Name, repoName)), nil
  154. }
  155. var (
  156. illegalEquals = []string{"debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new"}
  157. illegalSuffixs = []string{".git"}
  158. )
  159. // IsLegalName returns false if name contains illegal characters.
  160. func IsLegalName(repoName string) bool {
  161. repoName = strings.ToLower(repoName)
  162. for _, char := range illegalEquals {
  163. if repoName == char {
  164. return false
  165. }
  166. }
  167. for _, char := range illegalSuffixs {
  168. if strings.HasSuffix(repoName, char) {
  169. return false
  170. }
  171. }
  172. return true
  173. }
  174. // Mirror represents a mirror information of repository.
  175. type Mirror struct {
  176. Id int64
  177. RepoId int64
  178. RepoName string // <user name>/<repo name>
  179. Interval int // Hour.
  180. Updated time.Time `xorm:"UPDATED"`
  181. NextUpdate time.Time
  182. }
  183. func GetMirror(repoId int64) (*Mirror, error) {
  184. m := &Mirror{RepoId: repoId}
  185. has, err := x.Get(m)
  186. if err != nil {
  187. return nil, err
  188. } else if !has {
  189. return nil, ErrMirrorNotExist
  190. }
  191. return m, nil
  192. }
  193. func UpdateMirror(m *Mirror) error {
  194. _, err := x.Id(m.Id).Update(m)
  195. return err
  196. }
  197. // MirrorRepository creates a mirror repository from source.
  198. func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) error {
  199. _, stderr, err := process.ExecTimeout(10*time.Minute,
  200. fmt.Sprintf("MirrorRepository: %s/%s", userName, repoName),
  201. "git", "clone", "--mirror", url, repoPath)
  202. if err != nil {
  203. return errors.New("git clone --mirror: " + stderr)
  204. }
  205. if _, err = x.InsertOne(&Mirror{
  206. RepoId: repoId,
  207. RepoName: strings.ToLower(userName + "/" + repoName),
  208. Interval: 24,
  209. NextUpdate: time.Now().Add(24 * time.Hour),
  210. }); err != nil {
  211. return err
  212. }
  213. return nil
  214. }
  215. // MirrorUpdate checks and updates mirror repositories.
  216. func MirrorUpdate() {
  217. if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error {
  218. m := bean.(*Mirror)
  219. if m.NextUpdate.After(time.Now()) {
  220. return nil
  221. }
  222. repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git")
  223. if _, stderr, err := process.ExecDir(10*time.Minute,
  224. repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
  225. "git", "remote", "update"); err != nil {
  226. return errors.New("git remote update: " + stderr)
  227. }
  228. m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
  229. return UpdateMirror(m)
  230. }); err != nil {
  231. log.Error(4, "repo.MirrorUpdate: %v", err)
  232. }
  233. }
  234. // MigrateRepository migrates a existing repository from other project hosting.
  235. func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) {
  236. repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false)
  237. if err != nil {
  238. return nil, err
  239. }
  240. // Clone to temprory path and do the init commit.
  241. tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()))
  242. os.MkdirAll(tmpDir, os.ModePerm)
  243. repoPath := RepoPath(u.Name, name)
  244. repo.IsBare = false
  245. if mirror {
  246. if err = MirrorRepository(repo.Id, u.Name, repo.Name, repoPath, url); err != nil {
  247. return repo, err
  248. }
  249. repo.IsMirror = true
  250. return repo, UpdateRepository(repo)
  251. }
  252. // Clone from local repository.
  253. _, stderr, err := process.ExecTimeout(10*time.Minute,
  254. fmt.Sprintf("MigrateRepository(git clone): %s", repoPath),
  255. "git", "clone", repoPath, tmpDir)
  256. if err != nil {
  257. return repo, errors.New("git clone: " + stderr)
  258. }
  259. // Pull data from source.
  260. if _, stderr, err = process.ExecDir(3*time.Minute,
  261. tmpDir, fmt.Sprintf("MigrateRepository(git pull): %s", repoPath),
  262. "git", "pull", url); err != nil {
  263. return repo, errors.New("git pull: " + stderr)
  264. }
  265. // Push data to local repository.
  266. if _, stderr, err = process.ExecDir(3*time.Minute,
  267. tmpDir, fmt.Sprintf("MigrateRepository(git push): %s", repoPath),
  268. "git", "push", "origin", "master"); err != nil {
  269. return repo, errors.New("git push: " + stderr)
  270. }
  271. return repo, UpdateRepository(repo)
  272. }
  273. // extractGitBareZip extracts git-bare.zip to repository path.
  274. func extractGitBareZip(repoPath string) error {
  275. z, err := zip.Open(path.Join(setting.ConfRootPath, "content/git-bare.zip"))
  276. if err != nil {
  277. return err
  278. }
  279. defer z.Close()
  280. return z.ExtractTo(repoPath)
  281. }
  282. // initRepoCommit temporarily changes with work directory.
  283. func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
  284. var stderr string
  285. if _, stderr, err = process.ExecDir(-1,
  286. tmpPath, fmt.Sprintf("initRepoCommit(git add): %s", tmpPath),
  287. "git", "add", "--all"); err != nil {
  288. return errors.New("git add: " + stderr)
  289. }
  290. if _, stderr, err = process.ExecDir(-1,
  291. tmpPath, fmt.Sprintf("initRepoCommit(git commit): %s", tmpPath),
  292. "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
  293. "-m", "Init commit"); err != nil {
  294. return errors.New("git commit: " + stderr)
  295. }
  296. if _, stderr, err = process.ExecDir(-1,
  297. tmpPath, fmt.Sprintf("initRepoCommit(git push): %s", tmpPath),
  298. "git", "push", "origin", "master"); err != nil {
  299. return errors.New("git push: " + stderr)
  300. }
  301. return nil
  302. }
  303. func createHookUpdate(hookPath, content string) error {
  304. pu, err := os.OpenFile(hookPath, os.O_CREATE|os.O_WRONLY, 0777)
  305. if err != nil {
  306. return err
  307. }
  308. defer pu.Close()
  309. _, err = pu.WriteString(content)
  310. return err
  311. }
  312. // InitRepository initializes README and .gitignore if needed.
  313. func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLang, license string) error {
  314. repoPath := RepoPath(u.Name, repo.Name)
  315. // Create bare new repository.
  316. if err := extractGitBareZip(repoPath); err != nil {
  317. return err
  318. }
  319. // hook/post-update
  320. if err := createHookUpdate(filepath.Join(repoPath, "hooks", "update"),
  321. fmt.Sprintf(TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"")); err != nil {
  322. return err
  323. }
  324. // Initialize repository according to user's choice.
  325. fileName := map[string]string{}
  326. if initReadme {
  327. fileName["readme"] = "README.md"
  328. }
  329. if repoLang != "" {
  330. fileName["gitign"] = ".gitignore"
  331. }
  332. if license != "" {
  333. fileName["license"] = "LICENSE"
  334. }
  335. // Clone to temprory path and do the init commit.
  336. tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond()))
  337. os.MkdirAll(tmpDir, os.ModePerm)
  338. _, stderr, err := process.Exec(
  339. fmt.Sprintf("initRepository(git clone): %s", repoPath),
  340. "git", "clone", repoPath, tmpDir)
  341. if err != nil {
  342. return errors.New("initRepository(git clone): " + stderr)
  343. }
  344. // README
  345. if initReadme {
  346. defaultReadme := repo.Name + "\n" + strings.Repeat("=",
  347. utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description
  348. if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]),
  349. []byte(defaultReadme), 0644); err != nil {
  350. return err
  351. }
  352. }
  353. // .gitignore
  354. filePath := "conf/gitignore/" + repoLang
  355. if com.IsFile(filePath) {
  356. targetPath := path.Join(tmpDir, fileName["gitign"])
  357. if com.IsFile(filePath) {
  358. if err = com.Copy(filePath, targetPath); err != nil {
  359. return err
  360. }
  361. } else {
  362. // Check custom files.
  363. filePath = path.Join(setting.CustomPath, "conf/gitignore", repoLang)
  364. if com.IsFile(filePath) {
  365. if err := com.Copy(filePath, targetPath); err != nil {
  366. return err
  367. }
  368. }
  369. }
  370. } else {
  371. delete(fileName, "gitign")
  372. }
  373. // LICENSE
  374. filePath = "conf/license/" + license
  375. if com.IsFile(filePath) {
  376. targetPath := path.Join(tmpDir, fileName["license"])
  377. if com.IsFile(filePath) {
  378. if err = com.Copy(filePath, targetPath); err != nil {
  379. return err
  380. }
  381. } else {
  382. // Check custom files.
  383. filePath = path.Join(setting.CustomPath, "conf/license", license)
  384. if com.IsFile(filePath) {
  385. if err := com.Copy(filePath, targetPath); err != nil {
  386. return err
  387. }
  388. }
  389. }
  390. } else {
  391. delete(fileName, "license")
  392. }
  393. if len(fileName) == 0 {
  394. return nil
  395. }
  396. // Apply changes and commit.
  397. return initRepoCommit(tmpDir, u.NewGitSig())
  398. }
  399. // CreateRepository creates a repository for given user or organization.
  400. func CreateRepository(u *User, name, desc, lang, license string, private, mirror, initReadme bool) (*Repository, error) {
  401. if !IsLegalName(name) {
  402. return nil, ErrRepoNameIllegal
  403. }
  404. isExist, err := IsRepositoryExist(u, name)
  405. if err != nil {
  406. return nil, err
  407. } else if isExist {
  408. return nil, ErrRepoAlreadyExist
  409. }
  410. sess := x.NewSession()
  411. defer sess.Close()
  412. if err = sess.Begin(); err != nil {
  413. return nil, err
  414. }
  415. repo := &Repository{
  416. OwnerId: u.Id,
  417. Owner: u,
  418. Name: name,
  419. LowerName: strings.ToLower(name),
  420. Description: desc,
  421. IsPrivate: private,
  422. IsBare: lang == "" && license == "" && !initReadme,
  423. }
  424. if !repo.IsBare {
  425. repo.DefaultBranch = "master"
  426. }
  427. if _, err = sess.Insert(repo); err != nil {
  428. sess.Rollback()
  429. return nil, err
  430. }
  431. var t *Team // Owner team.
  432. mode := WRITABLE
  433. if mirror {
  434. mode = READABLE
  435. }
  436. access := &Access{
  437. UserName: u.LowerName,
  438. RepoName: strings.ToLower(path.Join(u.Name, repo.Name)),
  439. Mode: mode,
  440. }
  441. // Give access to all members in owner team.
  442. if u.IsOrganization() {
  443. t, err = u.GetOwnerTeam()
  444. if err != nil {
  445. sess.Rollback()
  446. return nil, err
  447. }
  448. us, err := GetTeamMembers(u.Id, t.Id)
  449. if err != nil {
  450. sess.Rollback()
  451. return nil, err
  452. }
  453. for _, u := range us {
  454. access.UserName = u.LowerName
  455. if _, err = sess.Insert(access); err != nil {
  456. sess.Rollback()
  457. return nil, err
  458. }
  459. }
  460. } else {
  461. if _, err = sess.Insert(access); err != nil {
  462. sess.Rollback()
  463. return nil, err
  464. }
  465. }
  466. if _, err = sess.Exec(
  467. "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil {
  468. sess.Rollback()
  469. return nil, err
  470. }
  471. // Update owner team info and count.
  472. if u.IsOrganization() {
  473. t.RepoIds += "$" + com.ToStr(repo.Id) + "|"
  474. t.NumRepos++
  475. if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
  476. sess.Rollback()
  477. return nil, err
  478. }
  479. }
  480. if err = sess.Commit(); err != nil {
  481. return nil, err
  482. }
  483. if u.IsOrganization() {
  484. ous, err := GetOrgUsersByOrgId(u.Id)
  485. if err != nil {
  486. log.Error(4, "repo.CreateRepository(GetOrgUsersByOrgId): %v", err)
  487. } else {
  488. for _, ou := range ous {
  489. if err = WatchRepo(ou.Uid, repo.Id, true); err != nil {
  490. log.Error(4, "repo.CreateRepository(WatchRepo): %v", err)
  491. }
  492. }
  493. }
  494. }
  495. if err = WatchRepo(u.Id, repo.Id, true); err != nil {
  496. log.Error(4, "WatchRepo2: %v", err)
  497. }
  498. if err = NewRepoAction(u, repo); err != nil {
  499. log.Error(4, "NewRepoAction: %v", err)
  500. }
  501. // No need for init mirror.
  502. if mirror {
  503. return repo, nil
  504. }
  505. repoPath := RepoPath(u.Name, repo.Name)
  506. if err = initRepository(repoPath, u, repo, initReadme, lang, license); err != nil {
  507. if err2 := os.RemoveAll(repoPath); err2 != nil {
  508. log.Error(4, "initRepository: %v", err)
  509. return nil, fmt.Errorf(
  510. "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)
  511. }
  512. return nil, fmt.Errorf("initRepository: %v", err)
  513. }
  514. _, stderr, err := process.ExecDir(-1,
  515. repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath),
  516. "git", "update-server-info")
  517. if err != nil {
  518. return nil, errors.New("CreateRepository(git update-server-info): " + stderr)
  519. }
  520. return repo, nil
  521. }
  522. // CountRepositories returns number of repositories.
  523. func CountRepositories() int64 {
  524. count, _ := x.Count(new(Repository))
  525. return count
  526. }
  527. // GetRepositoriesWithUsers returns given number of repository objects with offset.
  528. // It also auto-gets corresponding users.
  529. func GetRepositoriesWithUsers(num, offset int) ([]*Repository, error) {
  530. repos := make([]*Repository, 0, num)
  531. if err := x.Limit(num, offset).Asc("id").Find(&repos); err != nil {
  532. return nil, err
  533. }
  534. for _, repo := range repos {
  535. repo.Owner = &User{Id: repo.OwnerId}
  536. has, err := x.Get(repo.Owner)
  537. if err != nil {
  538. return nil, err
  539. } else if !has {
  540. return nil, ErrUserNotExist
  541. }
  542. }
  543. return repos, nil
  544. }
  545. // RepoPath returns repository path by given user and repository name.
  546. func RepoPath(userName, repoName string) string {
  547. return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".git")
  548. }
  549. // TransferOwnership transfers all corresponding setting from old user to new one.
  550. func TransferOwnership(u *User, newOwner string, repo *Repository) (err error) {
  551. newUser, err := GetUserByName(newOwner)
  552. if err != nil {
  553. return err
  554. }
  555. sess := x.NewSession()
  556. defer sess.Close()
  557. if err = sess.Begin(); err != nil {
  558. return err
  559. }
  560. if _, err = sess.Where("repo_name = ?", u.LowerName+"/"+repo.LowerName).
  561. And("user_name = ?", u.LowerName).Update(&Access{UserName: newUser.LowerName}); err != nil {
  562. sess.Rollback()
  563. return err
  564. }
  565. if _, err = sess.Where("repo_name = ?", u.LowerName+"/"+repo.LowerName).Update(&Access{
  566. RepoName: newUser.LowerName + "/" + repo.LowerName,
  567. }); err != nil {
  568. sess.Rollback()
  569. return err
  570. }
  571. // Update repository.
  572. repo.OwnerId = newUser.Id
  573. if _, err := sess.Id(repo.Id).Update(repo); err != nil {
  574. sess.Rollback()
  575. return err
  576. }
  577. // Update user repository number.
  578. rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?"
  579. if _, err = sess.Exec(rawSql, newUser.Id); err != nil {
  580. sess.Rollback()
  581. return err
  582. }
  583. rawSql = "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?"
  584. if _, err = sess.Exec(rawSql, u.Id); err != nil {
  585. sess.Rollback()
  586. return err
  587. }
  588. // Change repository directory name.
  589. if err = os.Rename(RepoPath(u.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
  590. sess.Rollback()
  591. return err
  592. }
  593. if err = sess.Commit(); err != nil {
  594. return err
  595. }
  596. // Add watch of new owner to repository.
  597. if !IsWatching(newUser.Id, repo.Id) {
  598. if err = WatchRepo(newUser.Id, repo.Id, true); err != nil {
  599. return err
  600. }
  601. }
  602. if err = TransferRepoAction(u, newUser, repo); err != nil {
  603. return err
  604. }
  605. return nil
  606. }
  607. // ChangeRepositoryName changes all corresponding setting from old repository name to new one.
  608. func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) {
  609. // Update accesses.
  610. accesses := make([]Access, 0, 10)
  611. if err = x.Find(&accesses, &Access{RepoName: strings.ToLower(userName + "/" + oldRepoName)}); err != nil {
  612. return err
  613. }
  614. sess := x.NewSession()
  615. defer sess.Close()
  616. if err = sess.Begin(); err != nil {
  617. return err
  618. }
  619. for i := range accesses {
  620. accesses[i].RepoName = userName + "/" + newRepoName
  621. if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil {
  622. return err
  623. }
  624. }
  625. // Change repository directory name.
  626. if err = os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)); err != nil {
  627. sess.Rollback()
  628. return err
  629. }
  630. return sess.Commit()
  631. }
  632. func UpdateRepository(repo *Repository) error {
  633. repo.LowerName = strings.ToLower(repo.Name)
  634. if len(repo.Description) > 255 {
  635. repo.Description = repo.Description[:255]
  636. }
  637. if len(repo.Website) > 255 {
  638. repo.Website = repo.Website[:255]
  639. }
  640. _, err := x.Id(repo.Id).AllCols().Update(repo)
  641. return err
  642. }
  643. // DeleteRepository deletes a repository for a user or orgnaztion.
  644. func DeleteRepository(userId, repoId int64, userName string) error {
  645. repo := &Repository{Id: repoId, OwnerId: userId}
  646. has, err := x.Get(repo)
  647. if err != nil {
  648. return err
  649. } else if !has {
  650. return ErrRepoNotExist
  651. }
  652. sess := x.NewSession()
  653. defer sess.Close()
  654. if err = sess.Begin(); err != nil {
  655. return err
  656. }
  657. if _, err = sess.Delete(&Repository{Id: repoId}); err != nil {
  658. sess.Rollback()
  659. return err
  660. }
  661. if _, err := sess.Delete(&Access{RepoName: strings.ToLower(path.Join(userName, repo.Name))}); err != nil {
  662. sess.Rollback()
  663. return err
  664. }
  665. if _, err := sess.Delete(&Action{RepoId: repo.Id}); err != nil {
  666. sess.Rollback()
  667. return err
  668. }
  669. if _, err = sess.Delete(&Watch{RepoId: repoId}); err != nil {
  670. sess.Rollback()
  671. return err
  672. }
  673. if _, err = sess.Delete(&Mirror{RepoId: repoId}); err != nil {
  674. sess.Rollback()
  675. return err
  676. }
  677. if _, err = sess.Delete(&IssueUser{RepoId: repoId}); err != nil {
  678. sess.Rollback()
  679. return err
  680. }
  681. if _, err = sess.Delete(&Milestone{RepoId: repoId}); err != nil {
  682. sess.Rollback()
  683. return err
  684. }
  685. if _, err = sess.Delete(&Release{RepoId: repoId}); err != nil {
  686. sess.Rollback()
  687. return err
  688. }
  689. // Delete comments.
  690. if err = x.Iterate(&Issue{RepoId: repoId}, func(idx int, bean interface{}) error {
  691. issue := bean.(*Issue)
  692. if _, err = sess.Delete(&Comment{IssueId: issue.Id}); err != nil {
  693. sess.Rollback()
  694. return err
  695. }
  696. return nil
  697. }); err != nil {
  698. sess.Rollback()
  699. return err
  700. }
  701. if _, err = sess.Delete(&Issue{RepoId: repoId}); err != nil {
  702. sess.Rollback()
  703. return err
  704. }
  705. rawSql := "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?"
  706. if _, err = sess.Exec(rawSql, userId); err != nil {
  707. sess.Rollback()
  708. return err
  709. }
  710. if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
  711. sess.Rollback()
  712. return err
  713. }
  714. return sess.Commit()
  715. }
  716. // GetRepositoryByRef returns a Repository specified by a GFM reference.
  717. // See https://help.github.com/articles/writing-on-github#references for more information on the syntax.
  718. func GetRepositoryByRef(ref string) (*Repository, error) {
  719. n := strings.IndexByte(ref, byte('/'))
  720. if n < 2 {
  721. return nil, ErrInvalidReference
  722. }
  723. userName, repoName := ref[:n], ref[n+1:]
  724. user, err := GetUserByName(userName)
  725. if err != nil {
  726. return nil, err
  727. }
  728. return GetRepositoryByName(user.Id, repoName)
  729. }
  730. // GetRepositoryByName returns the repository by given name under user if exists.
  731. func GetRepositoryByName(userId int64, repoName string) (*Repository, error) {
  732. repo := &Repository{
  733. OwnerId: userId,
  734. LowerName: strings.ToLower(repoName),
  735. }
  736. has, err := x.Get(repo)
  737. if err != nil {
  738. return nil, err
  739. } else if !has {
  740. return nil, ErrRepoNotExist
  741. }
  742. return repo, err
  743. }
  744. // GetRepositoryById returns the repository by given id if exists.
  745. func GetRepositoryById(id int64) (*Repository, error) {
  746. repo := &Repository{}
  747. has, err := x.Id(id).Get(repo)
  748. if err != nil {
  749. return nil, err
  750. } else if !has {
  751. return nil, ErrRepoNotExist
  752. }
  753. return repo, nil
  754. }
  755. // GetRepositories returns a list of repositories of given user.
  756. func GetRepositories(uid int64, private bool) ([]*Repository, error) {
  757. repos := make([]*Repository, 0, 10)
  758. sess := x.Desc("updated")
  759. if !private {
  760. sess.Where("is_private=?", false)
  761. }
  762. err := sess.Find(&repos, &Repository{OwnerId: uid})
  763. return repos, err
  764. }
  765. // GetRecentUpdatedRepositories returns the list of repositories that are recently updated.
  766. func GetRecentUpdatedRepositories() (repos []*Repository, err error) {
  767. err = x.Where("is_private=?", false).Limit(5).Desc("updated").Find(&repos)
  768. return repos, err
  769. }
  770. // GetRepositoryCount returns the total number of repositories of user.
  771. func GetRepositoryCount(user *User) (int64, error) {
  772. return x.Count(&Repository{OwnerId: user.Id})
  773. }
  774. // GetCollaboratorNames returns a list of user name of repository's collaborators.
  775. func GetCollaboratorNames(repoName string) ([]string, error) {
  776. accesses := make([]*Access, 0, 10)
  777. if err := x.Find(&accesses, &Access{RepoName: strings.ToLower(repoName)}); err != nil {
  778. return nil, err
  779. }
  780. names := make([]string, len(accesses))
  781. for i := range accesses {
  782. names[i] = accesses[i].UserName
  783. }
  784. return names, nil
  785. }
  786. // GetCollaborativeRepos returns a list of repositories that user is collaborator.
  787. func GetCollaborativeRepos(uname string) ([]*Repository, error) {
  788. uname = strings.ToLower(uname)
  789. accesses := make([]*Access, 0, 10)
  790. if err := x.Find(&accesses, &Access{UserName: uname}); err != nil {
  791. return nil, err
  792. }
  793. repos := make([]*Repository, 0, 10)
  794. for _, access := range accesses {
  795. infos := strings.Split(access.RepoName, "/")
  796. if infos[0] == uname {
  797. continue
  798. }
  799. u, err := GetUserByName(infos[0])
  800. if err != nil {
  801. return nil, err
  802. }
  803. repo, err := GetRepositoryByName(u.Id, infos[1])
  804. if err != nil {
  805. return nil, err
  806. }
  807. repo.Owner = u
  808. repos = append(repos, repo)
  809. }
  810. return repos, nil
  811. }
  812. // GetCollaborators returns a list of users of repository's collaborators.
  813. func GetCollaborators(repoName string) (us []*User, err error) {
  814. accesses := make([]*Access, 0, 10)
  815. if err = x.Find(&accesses, &Access{RepoName: strings.ToLower(repoName)}); err != nil {
  816. return nil, err
  817. }
  818. us = make([]*User, len(accesses))
  819. for i := range accesses {
  820. us[i], err = GetUserByName(accesses[i].UserName)
  821. if err != nil {
  822. return nil, err
  823. }
  824. }
  825. return us, nil
  826. }
  827. // Watch is connection request for receiving repository notifycation.
  828. type Watch struct {
  829. Id int64
  830. UserId int64 `xorm:"UNIQUE(watch)"`
  831. RepoId int64 `xorm:"UNIQUE(watch)"`
  832. }
  833. // Watch or unwatch repository.
  834. func WatchRepo(uid, rid int64, watch bool) (err error) {
  835. if watch {
  836. if _, err = x.Insert(&Watch{RepoId: rid, UserId: uid}); err != nil {
  837. return err
  838. }
  839. _, err = x.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", rid)
  840. } else {
  841. if _, err = x.Delete(&Watch{0, uid, rid}); err != nil {
  842. return err
  843. }
  844. _, err = x.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", rid)
  845. }
  846. return err
  847. }
  848. // GetWatchers returns all watchers of given repository.
  849. func GetWatchers(rid int64) ([]*Watch, error) {
  850. watches := make([]*Watch, 0, 10)
  851. err := x.Find(&watches, &Watch{RepoId: rid})
  852. return watches, err
  853. }
  854. // NotifyWatchers creates batch of actions for every watcher.
  855. func NotifyWatchers(act *Action) error {
  856. // Add feeds for user self and all watchers.
  857. watches, err := GetWatchers(act.RepoId)
  858. if err != nil {
  859. return errors.New("repo.NotifyWatchers(get watches): " + err.Error())
  860. }
  861. // Add feed for actioner.
  862. act.UserId = act.ActUserId
  863. if _, err = x.InsertOne(act); err != nil {
  864. return errors.New("repo.NotifyWatchers(create action): " + err.Error())
  865. }
  866. for i := range watches {
  867. if act.ActUserId == watches[i].UserId {
  868. continue
  869. }
  870. act.Id = 0
  871. act.UserId = watches[i].UserId
  872. if _, err = x.InsertOne(act); err != nil {
  873. return errors.New("repo.NotifyWatchers(create action): " + err.Error())
  874. }
  875. }
  876. return nil
  877. }
  878. // IsWatching checks if user has watched given repository.
  879. func IsWatching(uid, rid int64) bool {
  880. has, _ := x.Get(&Watch{0, uid, rid})
  881. return has
  882. }
  883. func ForkRepository(repoName string, uid int64) {
  884. }